From 0a8b7020b5bd207066243b47467dbf2fb12d0e87 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 05:33:04 +0900 Subject: [PATCH 01/21] Simplify null and empty check --- .../java/awais/instagrabber/utils/Utils.java | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 8efd41cf..ee218b3d 100755 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -121,29 +121,31 @@ public final class Utils { public static void setupCookies(final String cookieRaw) { final CookieStore cookieStore = NET_COOKIE_MANAGER.getCookieStore(); - if (cookieRaw == "LOGOUT") { - cookieStore.removeAll(); + if (cookieStore == null || isEmpty(cookieRaw)) { + return; } - else if (cookieRaw != null && !isEmpty(cookieRaw)) { - try { - final URI uri1 = new URI("https://instagram.com"); - final URI uri2 = new URI("https://instagram.com/"); - final URI uri3 = new URI("https://i.instagram.com/"); - for (final String cookie : cookieRaw.split(";")) { - final String[] strings = cookie.split("=", 2); - final HttpCookie httpCookie = new HttpCookie(strings[0].trim(), strings[1].trim()); - httpCookie.setDomain("instagram.com"); - httpCookie.setPath("/"); - httpCookie.setVersion(0); - cookieStore.add(uri1, httpCookie); - cookieStore.add(uri2, httpCookie); - cookieStore.add(uri3, httpCookie); - } - } catch (final URISyntaxException e) { - if (logCollector != null) - logCollector.appendException(e, LogCollector.LogFile.UTILS, "setupCookies"); - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); + if (cookieRaw.equals("LOGOUT")) { + cookieStore.removeAll(); + return; + } + try { + final URI uri1 = new URI("https://instagram.com"); + final URI uri2 = new URI("https://instagram.com/"); + final URI uri3 = new URI("https://i.instagram.com/"); + for (final String cookie : cookieRaw.split(";")) { + final String[] strings = cookie.split("=", 2); + final HttpCookie httpCookie = new HttpCookie(strings[0].trim(), strings[1].trim()); + httpCookie.setDomain("instagram.com"); + httpCookie.setPath("/"); + httpCookie.setVersion(0); + cookieStore.add(uri1, httpCookie); + cookieStore.add(uri2, httpCookie); + cookieStore.add(uri3, httpCookie); } + } catch (final URISyntaxException e) { + if (logCollector != null) + logCollector.appendException(e, LogCollector.LogFile.UTILS, "setupCookies"); + if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); } } From fb51995c853813ffcc6ed41e55322ba0324e5813 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 05:36:23 +0900 Subject: [PATCH 02/21] Rename DirectMesagesUserInbox to DirectMessageThread --- app/src/main/AndroidManifest.xml | 2 +- ...DirectMessagesUserInbox.java => DirectMessageThread.java} | 5 ++--- .../java/awais/instagrabber/activities/DirectMessages.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) rename app/src/main/java/awais/instagrabber/activities/{DirectMessagesUserInbox.java => DirectMessageThread.java} (98%) mode change 100755 => 100644 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f2047dbe..3fe3afef 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -224,7 +224,7 @@ { final Object tag = v.getTag(); if (tag instanceof InboxThreadModel) { - startActivity(new Intent(this, DirectMessagesUserInbox.class) + startActivity(new Intent(this, DirectMessageThread.class) .putExtra(Constants.EXTRAS_THREAD_MODEL, (InboxThreadModel) tag) ); } From 7baa8c2b4939c324275f45d9578e27615a6b0b8c Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 05:38:46 +0900 Subject: [PATCH 03/21] Move default ProfileModel object creation to ProfileModel class --- .../awais/instagrabber/activities/DirectMessageThread.java | 3 +-- app/src/main/java/awais/instagrabber/models/ProfileModel.java | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index d3e79086..97147eca 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -43,8 +43,7 @@ import static awais.instagrabber.utils.Utils.settingsHelper; public final class DirectMessageThread extends BaseLanguageActivity { private DirectItemModel directItemModel; - private final ProfileModel myProfileHolder = - new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false); + private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); private final ArrayList users = new ArrayList<>(), leftusers = new ArrayList<>(); private final ArrayList directItemModels = new ArrayList<>(); private String threadid; diff --git a/app/src/main/java/awais/instagrabber/models/ProfileModel.java b/app/src/main/java/awais/instagrabber/models/ProfileModel.java index f3a3fa7a..9b5ef936 100755 --- a/app/src/main/java/awais/instagrabber/models/ProfileModel.java +++ b/app/src/main/java/awais/instagrabber/models/ProfileModel.java @@ -31,6 +31,10 @@ public final class ProfileModel implements Serializable { this.requested = requested; } + public static ProfileModel getDefaultProfileModel() { + return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false); + } + public boolean isPrivate() { return isPrivate; } From 78955cd73f99a38c2fa73bdf30830bf14288b508 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 05:53:29 +0900 Subject: [PATCH 04/21] setup CommentAction to be moved to its own file --- .../activities/DirectMessageThread.java | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 97147eca..5162c986 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -207,13 +207,39 @@ public final class DirectMessageThread extends BaseLanguageActivity { private final View.OnClickListener newCommentListener = v -> { if (Utils.isEmpty(dmsBinding.commentText.getText().toString()) && v == dmsBinding.commentSend) Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); - else if (v == dmsBinding.commentSend) new CommentAction().execute(); + else if (v == dmsBinding.commentSend) { + final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); + action.setOnTaskCompleteListener(new CommentAction.OnTaskCompleteListener() { + @Override + public void onTaskComplete(boolean ok) { + if (!ok) { + Toast.makeText(getApplicationContext(), 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); + } + }); + action.execute(); + } }; - class CommentAction extends AsyncTask { - boolean ok = false; + public static class CommentAction extends AsyncTask { + private final String text; + private final String threadId; - protected Void doInBackground(Void... lmao) { + private OnTaskCompleteListener listener; + + public CommentAction(String text, String threadId) { + this.text = text; + this.threadId = threadId; + } + + protected Boolean doInBackground(Void... lmao) { + boolean ok = false; final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/"; final String cookie = settingsHelper.getString(Constants.COOKIE); try { @@ -221,7 +247,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { urlConnection2.setRequestMethod("POST"); urlConnection2.setRequestProperty("User-Agent", Constants.I_USER_AGENT); urlConnection2.setUseCaches(false); - final String commentText = URLEncoder.encode(dmsBinding.commentText.getText().toString(), "UTF-8") + final String commentText = URLEncoder.encode(text, "UTF-8") .replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'") .replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~"); final String cc = UUID.randomUUID().toString(); @@ -231,7 +257,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { +"\",\"client_context\":\"" + cc +"\",\"mutation_token\":\"" + cc +"\",\"text\":\"" + commentText - +"\",\"thread_ids\":\"["+threadid + +"\",\"thread_ids\":\"["+ threadId +"]\",\"action\":\"send_item\"}"); urlConnection2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); urlConnection2.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters2.getBytes().length)); @@ -249,19 +275,24 @@ public final class DirectMessageThread extends BaseLanguageActivity { } catch (Throwable ex) { Log.e("austin_debug", "dm send: " + ex); } - return null; + return ok; } @Override - protected void onPostExecute(Void result) { - if (!ok) Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - else { - dmsBinding.commentText.setText(""); - dmsBinding.commentText.clearFocus(); - directItemModels.clear(); - messageItemsAdapter.notifyDataSetChanged(); - new UserInboxFetcher(threadid, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + protected void onPostExecute(final Boolean result) { + if (listener != null) { + listener.onTaskComplete(result); } } + + public void setOnTaskCompleteListener(final OnTaskCompleteListener listener) { + if (listener != null) { + this.listener = listener; + } + } + + public interface OnTaskCompleteListener { + void onTaskComplete(boolean ok); + } } } \ No newline at end of file From 6c478d8626f5c5f46c15b0648ae1dd19068a270e Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 05:59:16 +0900 Subject: [PATCH 05/21] move CommentAction to its own file --- .../activities/DirectMessageThread.java | 90 +++---------------- .../asyncs/direct_messages/CommentAction.java | 84 +++++++++++++++++ 2 files changed, 94 insertions(+), 80 deletions(-) create mode 100644 app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 5162c986..ecdbddcf 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -24,6 +24,7 @@ import java.util.UUID; import awais.instagrabber.R; import awais.instagrabber.adapters.MessageItemsAdapter; +import awais.instagrabber.asyncs.direct_messages.CommentAction; import awais.instagrabber.asyncs.direct_messages.UserInboxFetcher; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.databinding.ActivityDmsBinding; @@ -209,90 +210,19 @@ public final class DirectMessageThread extends BaseLanguageActivity { Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); else if (v == dmsBinding.commentSend) { final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); - action.setOnTaskCompleteListener(new CommentAction.OnTaskCompleteListener() { - @Override - public void onTaskComplete(boolean ok) { - if (!ok) { - Toast.makeText(getApplicationContext(), 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); + action.setOnTaskCompleteListener(result -> { + if (!result) { + Toast.makeText(getApplicationContext(), 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); }); action.execute(); } }; - public static class CommentAction extends AsyncTask { - private final String text; - private final String threadId; - - private OnTaskCompleteListener listener; - - public CommentAction(String text, String threadId) { - this.text = text; - this.threadId = threadId; - } - - protected Boolean doInBackground(Void... lmao) { - boolean ok = false; - final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/"; - final String cookie = settingsHelper.getString(Constants.COOKIE); - try { - final HttpURLConnection urlConnection2 = (HttpURLConnection) new URL(url2).openConnection(); - urlConnection2.setRequestMethod("POST"); - urlConnection2.setRequestProperty("User-Agent", Constants.I_USER_AGENT); - urlConnection2.setUseCaches(false); - final String commentText = URLEncoder.encode(text, "UTF-8") - .replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'") - .replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~"); - final String cc = UUID.randomUUID().toString(); - final String urlParameters2 = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] - +"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie) - +"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID) - +"\",\"client_context\":\"" + cc - +"\",\"mutation_token\":\"" + cc - +"\",\"text\":\"" + commentText - +"\",\"thread_ids\":\"["+ threadId - +"]\",\"action\":\"send_item\"}"); - urlConnection2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - urlConnection2.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters2.getBytes().length)); - urlConnection2.setDoOutput(true); - DataOutputStream wr2 = new DataOutputStream(urlConnection2.getOutputStream()); - wr2.writeBytes(urlParameters2); - wr2.flush(); - wr2.close(); - urlConnection2.connect(); - Log.d("austin_debug", urlConnection2.getResponseCode() + " " + urlParameters2 + " " + cookie); - if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) { - ok = true; - } - urlConnection2.disconnect(); - } catch (Throwable ex) { - Log.e("austin_debug", "dm send: " + ex); - } - return ok; - } - - @Override - protected void onPostExecute(final Boolean result) { - if (listener != null) { - listener.onTaskComplete(result); - } - } - - public void setOnTaskCompleteListener(final OnTaskCompleteListener listener) { - if (listener != null) { - this.listener = listener; - } - } - - public interface OnTaskCompleteListener { - void onTaskComplete(boolean ok); - } - } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java new file mode 100644 index 00000000..6b6cc08c --- /dev/null +++ b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java @@ -0,0 +1,84 @@ +package awais.instagrabber.asyncs.direct_messages; + +import android.os.AsyncTask; +import android.util.Log; + +import java.io.DataOutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.UUID; + +import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.Utils; + +import static awais.instagrabber.utils.Utils.settingsHelper; + +public class CommentAction extends AsyncTask { + private final String text; + private final String threadId; + + private OnTaskCompleteListener listener; + + public CommentAction(String text, String threadId) { + this.text = text; + this.threadId = threadId; + } + + protected Boolean doInBackground(Void... lmao) { + boolean ok = false; + final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/"; + final String cookie = settingsHelper.getString(Constants.COOKIE); + try { + final HttpURLConnection urlConnection2 = (HttpURLConnection) new URL(url2).openConnection(); + urlConnection2.setRequestMethod("POST"); + urlConnection2.setRequestProperty("User-Agent", Constants.I_USER_AGENT); + urlConnection2.setUseCaches(false); + final String commentText = URLEncoder.encode(text, "UTF-8") + .replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'") + .replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~"); + final String cc = UUID.randomUUID().toString(); + final String urlParameters2 = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] + + "\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie) + + "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID) + + "\",\"client_context\":\"" + cc + + "\",\"mutation_token\":\"" + cc + + "\",\"text\":\"" + commentText + + "\",\"thread_ids\":\"[" + threadId + + "]\",\"action\":\"send_item\"}"); + urlConnection2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + urlConnection2.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters2.getBytes().length)); + urlConnection2.setDoOutput(true); + DataOutputStream wr2 = new DataOutputStream(urlConnection2.getOutputStream()); + wr2.writeBytes(urlParameters2); + wr2.flush(); + wr2.close(); + urlConnection2.connect(); + Log.d("austin_debug", urlConnection2.getResponseCode() + " " + urlParameters2 + " " + cookie); + if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) { + ok = true; + } + urlConnection2.disconnect(); + } catch (Throwable ex) { + Log.e("austin_debug", "dm send: " + ex); + } + return ok; + } + + @Override + protected void onPostExecute(final Boolean result) { + if (listener != null) { + listener.onTaskComplete(result); + } + } + + public void setOnTaskCompleteListener(final OnTaskCompleteListener listener) { + if (listener != null) { + this.listener = listener; + } + } + + public interface OnTaskCompleteListener { + void onTaskComplete(boolean ok); + } +} From 4e00d02f17db37bf7dc0e13bb950a1ef3d688fb1 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:00:30 +0900 Subject: [PATCH 06/21] reformat code --- .../activities/DirectMessageThread.java | 20 +++++-------------- .../instagrabber/models/ProfileModel.java | 16 +++++++++++---- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index ecdbddcf..030b61e1 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -12,15 +12,10 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import java.io.DataOutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.UUID; import awais.instagrabber.R; import awais.instagrabber.adapters.MessageItemsAdapter; @@ -40,8 +35,6 @@ import awais.instagrabber.models.enums.UserInboxDirection; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Utils; -import static awais.instagrabber.utils.Utils.settingsHelper; - public final class DirectMessageThread extends BaseLanguageActivity { private DirectItemModel directItemModel; private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); @@ -61,7 +54,8 @@ public final class DirectMessageThread extends BaseLanguageActivity { if (result != null) { endCursor = result.getPrevCursor(); - if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) endCursor = null; + if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) + endCursor = null; users.clear(); users.addAll(Arrays.asList(result.getUsers())); @@ -162,8 +156,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { .putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName()) .putExtra(Constants.EXTRAS_STORIES, sms) ); - } - else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) { + } else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) { searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]); } break; @@ -172,13 +165,10 @@ public final class DirectMessageThread extends BaseLanguageActivity { searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]); break; default: - Log.d("austin_debug", "unsupported type "+itemType); + Log.d("austin_debug", "unsupported type " + itemType); } } - }, - (view, text, isHashtag) -> { - searchUsername(text); - }); + }, (view, text, isHashtag) -> searchUsername(text)); dmsBinding.rvDirectMessages.setAdapter(messageItemsAdapter); diff --git a/app/src/main/java/awais/instagrabber/models/ProfileModel.java b/app/src/main/java/awais/instagrabber/models/ProfileModel.java index 9b5ef936..027cbf03 100755 --- a/app/src/main/java/awais/instagrabber/models/ProfileModel.java +++ b/app/src/main/java/awais/instagrabber/models/ProfileModel.java @@ -75,17 +75,25 @@ public final class ProfileModel implements Serializable { return hdProfilePic; } - public long getPostCount() { return postCount; } + public long getPostCount() { + return postCount; + } - public long getFollowersCount() { return followersCount; } + public long getFollowersCount() { + return followersCount; + } public long getFollowingCount() { return followingCount; } - public boolean getFollowing() { return following; } + public boolean getFollowing() { + return following; + } - public boolean getRestricted() { return restricted; } + public boolean getRestricted() { + return restricted; + } public boolean getBlocked() { return blocked; From 86b9feda4a2000f64cb796eff76b330e298bce8b Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:02:46 +0900 Subject: [PATCH 07/21] organize code for readability --- .../activities/DirectMessageThread.java | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 030b61e1..9e0c0ff8 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -37,10 +37,15 @@ import awais.instagrabber.utils.Utils; public final class DirectMessageThread extends BaseLanguageActivity { private DirectItemModel directItemModel; - private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); - private final ArrayList users = new ArrayList<>(), leftusers = new ArrayList<>(); - private final ArrayList directItemModels = new ArrayList<>(); private String threadid; + private String endCursor; + private ActivityDmsBinding dmsBinding; + private MessageItemsAdapter messageItemsAdapter; + + 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() { @@ -80,9 +85,25 @@ public final class DirectMessageThread extends BaseLanguageActivity { dmsBinding.swipeRefreshLayout.setRefreshing(false); } }; - private String endCursor; - private ActivityDmsBinding dmsBinding; - private MessageItemsAdapter messageItemsAdapter; + private final View.OnClickListener newCommentListener = v -> { + if (Utils.isEmpty(dmsBinding.commentText.getText().toString()) && v == dmsBinding.commentSend) + Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); + else if (v == dmsBinding.commentSend) { + final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); + action.setOnTaskCompleteListener(result -> { + if (!result) { + Toast.makeText(getApplicationContext(), 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); + }); + action.execute(); + } + }; @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { @@ -194,25 +215,4 @@ public final class DirectMessageThread extends BaseLanguageActivity { private void searchUsername(final String text) { startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text)); } - - private final View.OnClickListener newCommentListener = v -> { - if (Utils.isEmpty(dmsBinding.commentText.getText().toString()) && v == dmsBinding.commentSend) - Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); - else if (v == dmsBinding.commentSend) { - final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); - action.setOnTaskCompleteListener(result -> { - if (!result) { - Toast.makeText(getApplicationContext(), 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); - }); - action.execute(); - } - }; - } \ No newline at end of file From 1a57969abe6854434c45f6581e40aec969625bd1 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:04:50 +0900 Subject: [PATCH 08/21] simplify logic --- .../instagrabber/activities/DirectMessageThread.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 9e0c0ff8..50c65708 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -86,9 +86,11 @@ public final class DirectMessageThread extends BaseLanguageActivity { } }; private final View.OnClickListener newCommentListener = v -> { - if (Utils.isEmpty(dmsBinding.commentText.getText().toString()) && v == dmsBinding.commentSend) - Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); - else if (v == dmsBinding.commentSend) { + if (v == dmsBinding.commentSend) { + if (Utils.isEmpty(dmsBinding.commentText.getText().toString())) { + Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); + return; + } final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); action.setOnTaskCompleteListener(result -> { if (!result) { From 2a068c4feb84a37306e6e998d6e5a0ad5471bed4 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:06:14 +0900 Subject: [PATCH 09/21] remove view check as listener attached only to that view --- .../activities/DirectMessageThread.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 50c65708..abe771da 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -86,25 +86,23 @@ public final class DirectMessageThread extends BaseLanguageActivity { } }; private final View.OnClickListener newCommentListener = v -> { - if (v == dmsBinding.commentSend) { - if (Utils.isEmpty(dmsBinding.commentText.getText().toString())) { - Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); + if (Utils.isEmpty(dmsBinding.commentText.getText().toString())) { + Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); + return; + } + final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); + action.setOnTaskCompleteListener(result -> { + if (!result) { + Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); return; } - final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); - action.setOnTaskCompleteListener(result -> { - if (!result) { - Toast.makeText(getApplicationContext(), 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); - }); - action.execute(); - } + dmsBinding.commentText.setText(""); + dmsBinding.commentText.clearFocus(); + directItemModels.clear(); + messageItemsAdapter.notifyDataSetChanged(); + new UserInboxFetcher(threadid, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }); + action.execute(); }; @Override From 0023c566888afe1f4200f036e92cce37af00e8c3 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:08:25 +0900 Subject: [PATCH 10/21] rename variables to camelCase --- .../activities/DirectMessageThread.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index abe771da..3574856e 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -37,14 +37,14 @@ import awais.instagrabber.utils.Utils; public final class DirectMessageThread extends BaseLanguageActivity { private DirectItemModel directItemModel; - private String threadid; + private String threadId; private String endCursor; private ActivityDmsBinding dmsBinding; private MessageItemsAdapter messageItemsAdapter; private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); private final ArrayList users = new ArrayList<>(); - private final ArrayList leftusers = new ArrayList<>(); + private final ArrayList leftUsers = new ArrayList<>(); private final ArrayList directItemModels = new ArrayList<>(); private final FetchListener fetchListener = new FetchListener() { @Override @@ -65,10 +65,10 @@ public final class DirectMessageThread extends BaseLanguageActivity { users.clear(); users.addAll(Arrays.asList(result.getUsers())); - leftusers.clear(); - leftusers.addAll(Arrays.asList(result.getLeftUsers())); + leftUsers.clear(); + leftUsers.addAll(Arrays.asList(result.getLeftUsers())); - threadid = result.getThreadId(); + threadId = result.getThreadId(); dmsBinding.toolbar.toolbar.setTitle(result.getThreadTitle()); String[] users = new String[result.getUsers().length]; for (int i = 0; i < users.length; ++i) { @@ -90,7 +90,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); return; } - final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadid); + final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadId); action.setOnTaskCompleteListener(result -> { if (!result) { Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); @@ -100,7 +100,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { dmsBinding.commentText.clearFocus(); directItemModels.clear(); messageItemsAdapter.notifyDataSetChanged(); - new UserInboxFetcher(threadid, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new UserInboxFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }); action.execute(); }; @@ -134,7 +134,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { } })); - messageItemsAdapter = new MessageItemsAdapter(directItemModels, users, leftusers, v -> { + messageItemsAdapter = new MessageItemsAdapter(directItemModels, users, leftUsers, v -> { Object tag = v.getTag(); if (tag instanceof DirectItemModel) { directItemModel = (DirectItemModel) tag; @@ -203,9 +203,9 @@ public final class DirectMessageThread extends BaseLanguageActivity { 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; + if (leftUsers != null) + for (final ProfileModel leftUser : leftUsers) { + if (Long.toString(userId).equals(leftUser.getId())) result = leftUser; } return result; } From a0402c8bf3511bcf966a67ee4d39d0f6387900ae Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:14:11 +0900 Subject: [PATCH 11/21] Use TextUtils.join as String.join requires API level 26 --- .../awais/instagrabber/activities/DirectMessageThread.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 3574856e..4b2c0b5b 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -4,6 +4,7 @@ import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Toast; @@ -74,7 +75,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { for (int i = 0; i < users.length; ++i) { users[i] = result.getUsers()[i].getUsername(); } - dmsBinding.toolbar.toolbar.setSubtitle(String.join(", ", users)); + dmsBinding.toolbar.toolbar.setSubtitle(TextUtils.join(", ", users)); final int oldSize = directItemModels.size(); final List itemModels = Arrays.asList(result.getItems()); From 8e4ae8fe09ae4ef1b5834d350de16de1aa59d222 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:14:57 +0900 Subject: [PATCH 12/21] null check --- .../awais/instagrabber/activities/DirectMessageThread.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 4b2c0b5b..2230e064 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -157,8 +157,10 @@ public final class DirectMessageThread extends BaseLanguageActivity { break; case RAVEN_MEDIA: case MEDIA: - Utils.dmDownload(this, getUser(directItemModel.getUserId()).getUsername(), DownloadMethod.DOWNLOAD_DIRECT, - Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia())); + 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())); + } Toast.makeText(v.getContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show(); break; case STORY_SHARE: From 68dbf59ef6cfae26e075d075a6685562e1a7ba03 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:25:57 +0900 Subject: [PATCH 13/21] Add image upload icon to dms layout and handle click --- .../activities/DirectMessageThread.java | 87 +++++++++++++++---- app/src/main/res/drawable/ic_image_24.xml | 10 +++ app/src/main/res/drawable/ic_send_24.xml | 10 +++ app/src/main/res/layout/activity_dms.xml | 41 ++++++--- app/src/main/res/values/strings.xml | 1 + 5 files changed, 120 insertions(+), 29 deletions(-) create mode 100644 app/src/main/res/drawable/ic_image_24.xml create mode 100644 app/src/main/res/drawable/ic_send_24.xml diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java index 2230e064..80a42dea 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessageThread.java @@ -1,9 +1,12 @@ package awais.instagrabber.activities; +import android.app.Activity; import android.content.Intent; +import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.provider.OpenableColumns; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -13,6 +16,8 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -36,7 +41,12 @@ 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"; + private static final int PICK_IMAGE = 100; + private DirectItemModel directItemModel; private String threadId; private String endCursor; @@ -86,24 +96,34 @@ public final class DirectMessageThread extends BaseLanguageActivity { dmsBinding.swipeRefreshLayout.setRefreshing(false); } }; - private final View.OnClickListener newCommentListener = v -> { - if (Utils.isEmpty(dmsBinding.commentText.getText().toString())) { - Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); - return; - } - final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadId); - action.setOnTaskCompleteListener(result -> { - if (!result) { - Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + private final View.OnClickListener clickListener = v -> { + if (v == dmsBinding.commentSend) { + if (Utils.isEmpty(dmsBinding.commentText.getText().toString())) { + Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, 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); - }); - action.execute(); + final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadId); + action.setOnTaskCompleteListener(result -> { + if (!result) { + Toast.makeText(getApplicationContext(), 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); + }); + action.execute(); + return; + } + if (v == dmsBinding.image) { + final Intent intent = new Intent(); + intent.setType("image/*"); + intent.setAction(Intent.ACTION_GET_CONTENT); + startActivityForResult(Intent.createChooser(intent, getString(R.string.select_picture)), PICK_IMAGE); + } + }; @Override @@ -121,9 +141,11 @@ public final class DirectMessageThread extends BaseLanguageActivity { } dmsBinding.swipeRefreshLayout.setEnabled(false); - dmsBinding.commentText.setVisibility(View.VISIBLE); - dmsBinding.commentSend.setVisibility(View.VISIBLE); - dmsBinding.commentSend.setOnClickListener(newCommentListener); + dmsBinding.commentText.setVisibility(VISIBLE); + dmsBinding.commentSend.setVisibility(VISIBLE); + dmsBinding.image.setVisibility(VISIBLE); + dmsBinding.commentSend.setOnClickListener(clickListener); + dmsBinding.image.setOnClickListener(clickListener); final LinearLayoutManager layoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, true); dmsBinding.rvDirectMessages.setLayoutManager(layoutManager); @@ -199,6 +221,33 @@ public final class DirectMessageThread extends BaseLanguageActivity { new UserInboxFetcher(threadModel.getThreadId(), UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + @Override + protected 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) { + Log.w(TAG, "data is null!"); + return; + } + Cursor cursor = null; + try { + final Uri uri = data.getData(); + cursor = getContentResolver().query(uri, null, null, null, null); + if (cursor != null) { + final int contentLength = cursor.getColumnIndex(OpenableColumns.SIZE); + final InputStream inputStream = getContentResolver().openInputStream(uri); + // TODO Handle image upload + } + } catch (FileNotFoundException e) { + Log.e(TAG, "Error opening InputStream", e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + } + @Nullable private ProfileModel getUser(final long userId) { if (users != null) { diff --git a/app/src/main/res/drawable/ic_image_24.xml b/app/src/main/res/drawable/ic_image_24.xml new file mode 100644 index 00000000..a740230c --- /dev/null +++ b/app/src/main/res/drawable/ic_image_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_send_24.xml b/app/src/main/res/drawable/ic_send_24.xml new file mode 100644 index 00000000..fe37f93f --- /dev/null +++ b/app/src/main/res/drawable/ic_send_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_dms.xml b/app/src/main/res/layout/activity_dms.xml index 1f2f2c48..6bd5f945 100755 --- a/app/src/main/res/layout/activity_dms.xml +++ b/app/src/main/res/layout/activity_dms.xml @@ -9,14 +9,13 @@ + android:layout_height="0dp" + android:layout_weight="8"> + android:paddingStart="8dp" + android:paddingLeft="8dp" + android:paddingEnd="4dp" + android:paddingRight="4dp" + android:scrollHorizontally="false" + android:visibility="visible" /> + + - + android:paddingBottom="4dp" + android:visibility="visible" + app:srcCompat="@drawable/ic_send_24" /> \ 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 984f5ee9..a7682f9d 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -213,4 +213,5 @@ Use AMOLED mode for Dark theme 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 From 066a453aa898a90a202e1d57ca06d2a7eb955045 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Sun, 16 Aug 2020 06:31:55 +0900 Subject: [PATCH 14/21] move dm related Activities to their own package --- app/src/main/AndroidManifest.xml | 8 ++++---- app/src/main/java/awais/instagrabber/activities/Main.java | 1 + .../{ => directmessages}/DirectMessageThread.java | 6 +++++- .../activities/{ => directmessages}/DirectMessages.java | 3 ++- app/src/main/res/layout/activity_dms.xml | 2 +- 5 files changed, 13 insertions(+), 7 deletions(-) rename app/src/main/java/awais/instagrabber/activities/{ => directmessages}/DirectMessageThread.java (98%) rename app/src/main/java/awais/instagrabber/activities/{ => directmessages}/DirectMessages.java (97%) mode change 100755 => 100644 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3fe3afef..43696544 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -215,7 +215,7 @@ + android:name=".activities.directmessages.DirectMessageThread" + android:parentActivityName=".activities.directmessages.DirectMessages"> + android:value=".activities.directmessages.DirectMessages" /> + tools:context=".activities.directmessages.DirectMessages"> Date: Sun, 16 Aug 2020 18:21:09 +0900 Subject: [PATCH 15/21] Allow sending image in dms This commit streamlines the broadcast flow to handle both text and image messages in dms. --- .../directmessages/DirectMessageThread.java | 121 +++++++--- .../instagrabber/asyncs/ImageUploader.java | 167 ++++++++++++++ .../asyncs/direct_messages/CommentAction.java | 84 ------- .../DirectThreadBroadcaster.java | 207 ++++++++++++++++++ .../models/ImageUploadOptions.java | 76 +++++++ .../java/awais/instagrabber/utils/Utils.java | 23 +- 6 files changed, 553 insertions(+), 125 deletions(-) create mode 100644 app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java delete mode 100644 app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java create mode 100644 app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java create mode 100644 app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java diff --git a/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessageThread.java index bbec718a..7a374a2d 100644 --- a/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessageThread.java @@ -2,11 +2,10 @@ package awais.instagrabber.activities.directmessages; import android.app.Activity; import android.content.Intent; -import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.provider.OpenableColumns; +import android.os.ParcelFileDescriptor; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -16,8 +15,13 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.FileNotFoundException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -29,11 +33,17 @@ import awais.instagrabber.activities.PostViewer; import awais.instagrabber.activities.ProfileViewer; import awais.instagrabber.activities.StoryViewer; import awais.instagrabber.adapters.MessageItemsAdapter; -import awais.instagrabber.asyncs.direct_messages.CommentAction; +import awais.instagrabber.asyncs.ImageUploader; +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.interfaces.FetchListener; +import awais.instagrabber.models.ImageUploadOptions; import awais.instagrabber.models.PostModel; import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.StoryModel; @@ -102,23 +112,12 @@ public final class DirectMessageThread extends BaseLanguageActivity { }; private final View.OnClickListener clickListener = v -> { if (v == dmsBinding.commentSend) { - if (Utils.isEmpty(dmsBinding.commentText.getText().toString())) { + final String text = dmsBinding.commentText.getText().toString(); + if (Utils.isEmpty(text)) { Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); return; } - final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadId); - action.setOnTaskCompleteListener(result -> { - if (!result) { - Toast.makeText(getApplicationContext(), 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); - }); - action.execute(); + sendText(text); return; } if (v == dmsBinding.image) { @@ -127,7 +126,6 @@ public final class DirectMessageThread extends BaseLanguageActivity { intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, getString(R.string.select_picture)), PICK_IMAGE); } - }; @Override @@ -228,27 +226,13 @@ public final class DirectMessageThread extends BaseLanguageActivity { @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) { + if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) { if (data == null || data.getData() == null) { Log.w(TAG, "data is null!"); return; } - Cursor cursor = null; - try { - final Uri uri = data.getData(); - cursor = getContentResolver().query(uri, null, null, null, null); - if (cursor != null) { - final int contentLength = cursor.getColumnIndex(OpenableColumns.SIZE); - final InputStream inputStream = getContentResolver().openInputStream(uri); - // TODO Handle image upload - } - } catch (FileNotFoundException e) { - Log.e(TAG, "Error opening InputStream", e); - } finally { - if (cursor != null) { - cursor.close(); - } - } + final Uri uri = data.getData(); + sendImage(uri); } } @@ -271,4 +255,71 @@ public final class DirectMessageThread extends BaseLanguageActivity { private void searchUsername(final String text) { startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text)); } + + private void sendText(final String text) { + final TextBroadcastOptions options; + try { + options = new 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(); + return; + } + dmsBinding.commentText.setText(""); + dmsBinding.commentText.clearFocus(); + directItemModels.clear(); + messageItemsAdapter.notifyDataSetChanged(); + new UserInboxFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }); + } + + private void sendImage(final Uri imageUri) { + try { + final ParcelFileDescriptor fileDescriptor = 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); + // 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(); + if (response != null && response.getResponse() != null) { + Log.e(TAG, response.getResponse().toString()); + } + return; + } + final JSONObject responseJson = response.getResponse(); + 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); + }); + } catch (JSONException e) { + Log.e(TAG, "Error parsing json response", e); + } + }); + final ImageUploadOptions options = ImageUploadOptions.builder(inputStream, contentLength).build(); + imageUploader.execute(options); + } catch (FileNotFoundException e) { + Log.e(TAG, "Error opening InputStream", e); + } + } + + private void broadcast(final BroadcastOptions broadcastOptions, final OnBroadcastCompleteListener listener) { + final DirectThreadBroadcaster broadcaster = new DirectThreadBroadcaster(threadId); + broadcaster.setOnTaskCompleteListener(listener); + broadcaster.execute(broadcastOptions); + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java b/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java new file mode 100644 index 00000000..41454158 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java @@ -0,0 +1,167 @@ +package awais.instagrabber.asyncs; + +import android.os.AsyncTask; +import android.util.Log; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +import awais.instagrabber.models.ImageUploadOptions; +import awais.instagrabber.utils.Utils; + +public class ImageUploader extends AsyncTask { + private static final String TAG = "ImageUploader"; + private static final long LOWER = 1000000000L; + private static final long UPPER = 9999999999L; + private OnImageUploadCompleteListener listener; + + protected ImageUploadResponse doInBackground(final ImageUploadOptions... imageUploadOptions) { + if (imageUploadOptions == null || imageUploadOptions.length == 0 || imageUploadOptions[0] == null) { + return null; + } + HttpURLConnection connection = null; + OutputStream out = null; + InputStream inputStream = null; + BufferedReader r = null; + try { + final ImageUploadOptions options = imageUploadOptions[0]; + final Map headers = new HashMap<>(); + final String uploadId = String.valueOf(new Date().getTime()); + final long random = LOWER + new Random().nextLong() * (UPPER - LOWER + 1); + final String name = String.format("%s_0_%s", uploadId, random); + final String contentLength = String.valueOf(options.getContentLength()); + final String waterfallId = options.getWaterfallId() != null ? options.getWaterfallId() : UUID.randomUUID().toString(); + headers.put("X-Entity-Type", "image/jpeg"); + headers.put("Offset", "0"); + headers.put("X_FB_PHOTO_WATERFALL_ID", waterfallId); + headers.put("X-Instagram-Rupload-Params", new JSONObject(createPhotoRuploadParams(options, uploadId)).toString()); + headers.put("X-Entity-Name", name); + headers.put("X-Entity-Length", contentLength); + headers.put("Content-Type", "application/octet-stream"); + headers.put("Content-Length", contentLength); + headers.put("Accept-Encoding", "gzip"); + final String url = "https://www.instagram.com/rupload_igphoto/" + name + "/"; + connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("POST"); + connection.setUseCaches(false); + connection.setDoOutput(true); + Utils.setConnectionHeaders(connection, headers); + out = connection.getOutputStream(); + byte[] buffer = new byte[1024]; + int n; + inputStream = options.getInputStream(); + while (-1 != (n = inputStream.read(buffer))) { + out.write(buffer, 0, n); + } + out.flush(); + final int responseCode = connection.getResponseCode(); + Log.d(TAG, "response: " + responseCode); + if (responseCode != HttpURLConnection.HTTP_OK) { + return new ImageUploadResponse(responseCode, null); + } + r = new BufferedReader(new InputStreamReader(connection.getInputStream())); + final StringBuilder builder = new StringBuilder(); + for (String line = r.readLine(); line != null; line = r.readLine()) { + if (builder.length() != 0) { + builder.append("\n"); + } + builder.append(line); + } + return new ImageUploadResponse(responseCode, new JSONObject(builder.toString())); + } catch (Exception ex) { + Log.e(TAG, "Image upload error:", ex); + } finally { + if (r != null) { + try { + r.close(); + } catch (IOException ignored) { + } + } + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ignored) { + } + } + if (out != null) { + try { + out.close(); + } catch (IOException ignored) { + } + } + if (connection != null) { + connection.disconnect(); + } + } + return null; + } + + @Override + protected void onPostExecute(final ImageUploadResponse response) { + if (listener != null) { + listener.onImageUploadComplete(response); + } + } + + private Map createPhotoRuploadParams(final ImageUploadOptions options, final String uploadId) { + final Map retryContext = new HashMap<>(); + retryContext.put("num_step_auto_retry", 0); + retryContext.put("num_reupload", 0); + retryContext.put("num_step_manual_retry", 0); + final String retryContextString = new JSONObject(retryContext).toString(); + final Map params = new HashMap<>(); + params.put("retry_context", retryContextString); + params.put("media_type", "1"); + params.put("upload_id", uploadId); + params.put("xsharing_user_ids", "[]"); + final Map imageCompression = new HashMap<>(); + imageCompression.put("lib_name", "moz"); + imageCompression.put("lib_version", "3.1.m"); + imageCompression.put("quality", "80"); + params.put("image_compression", new JSONObject(imageCompression).toString()); + if (options.isSidecar()) { + params.put("is_sidecar", "1"); + } + return params; + } + + public void setOnTaskCompleteListener(final OnImageUploadCompleteListener listener) { + if (listener != null) { + this.listener = listener; + } + } + + public interface OnImageUploadCompleteListener { + void onImageUploadComplete(ImageUploadResponse response); + } + + public static class ImageUploadResponse { + private int responseCode; + private JSONObject response; + + public ImageUploadResponse(int responseCode, JSONObject response) { + this.responseCode = responseCode; + this.response = response; + } + + public int getResponseCode() { + return responseCode; + } + + public JSONObject getResponse() { + return response; + } + } +} diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java deleted file mode 100644 index 6b6cc08c..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CommentAction.java +++ /dev/null @@ -1,84 +0,0 @@ -package awais.instagrabber.asyncs.direct_messages; - -import android.os.AsyncTask; -import android.util.Log; - -import java.io.DataOutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.util.UUID; - -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.Utils; - -import static awais.instagrabber.utils.Utils.settingsHelper; - -public class CommentAction extends AsyncTask { - private final String text; - private final String threadId; - - private OnTaskCompleteListener listener; - - public CommentAction(String text, String threadId) { - this.text = text; - this.threadId = threadId; - } - - protected Boolean doInBackground(Void... lmao) { - boolean ok = false; - final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/"; - final String cookie = settingsHelper.getString(Constants.COOKIE); - try { - final HttpURLConnection urlConnection2 = (HttpURLConnection) new URL(url2).openConnection(); - urlConnection2.setRequestMethod("POST"); - urlConnection2.setRequestProperty("User-Agent", Constants.I_USER_AGENT); - urlConnection2.setUseCaches(false); - final String commentText = URLEncoder.encode(text, "UTF-8") - .replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'") - .replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~"); - final String cc = UUID.randomUUID().toString(); - final String urlParameters2 = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0] - + "\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie) - + "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID) - + "\",\"client_context\":\"" + cc - + "\",\"mutation_token\":\"" + cc - + "\",\"text\":\"" + commentText - + "\",\"thread_ids\":\"[" + threadId - + "]\",\"action\":\"send_item\"}"); - urlConnection2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - urlConnection2.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters2.getBytes().length)); - urlConnection2.setDoOutput(true); - DataOutputStream wr2 = new DataOutputStream(urlConnection2.getOutputStream()); - wr2.writeBytes(urlParameters2); - wr2.flush(); - wr2.close(); - urlConnection2.connect(); - Log.d("austin_debug", urlConnection2.getResponseCode() + " " + urlParameters2 + " " + cookie); - if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) { - ok = true; - } - urlConnection2.disconnect(); - } catch (Throwable ex) { - Log.e("austin_debug", "dm send: " + ex); - } - return ok; - } - - @Override - protected void onPostExecute(final Boolean result) { - if (listener != null) { - listener.onTaskComplete(result); - } - } - - public void setOnTaskCompleteListener(final OnTaskCompleteListener listener) { - if (listener != null) { - this.listener = listener; - } - } - - public interface OnTaskCompleteListener { - void onTaskComplete(boolean ok); - } -} 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 new file mode 100644 index 00000000..98542cbd --- /dev/null +++ b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java @@ -0,0 +1,207 @@ +package awais.instagrabber.asyncs.direct_messages; + +import android.os.AsyncTask; +import android.util.Log; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.Utils; + +import static awais.instagrabber.utils.Utils.settingsHelper; + +public class DirectThreadBroadcaster extends AsyncTask { + private static final String TAG = "DirectThreadBroadcaster"; + + private final String threadId; + + private OnBroadcastCompleteListener listener; + + public DirectThreadBroadcaster(String threadId) { + this.threadId = threadId; + } + + @Override + protected DirectThreadBroadcastResponse doInBackground(final BroadcastOptions... broadcastOptionsArray) { + if (broadcastOptionsArray == null || broadcastOptionsArray.length == 0 || broadcastOptionsArray[0] == null) { + return null; + } + final BroadcastOptions broadcastOptions = broadcastOptionsArray[0]; + final String cookie = settingsHelper.getString(Constants.COOKIE); + final String cc = UUID.randomUUID().toString(); + final Map form = new HashMap<>(); + form.put("_csrftoken", cookie.split("csrftoken=")[1].split(";")[0]); + form.put("_uid", Utils.getUserIdFromCookie(cookie)); + form.put("__uuid", settingsHelper.getString(Constants.DEVICE_UUID)); + form.put("client_context", cc); + form.put("mutation_token", cc); + form.putAll(broadcastOptions.getFormMap()); + form.put("thread_ids", String.format("[%s]", threadId)); + form.put("action", "send_item"); + final String message = new JSONObject(form).toString(); + final String content = Utils.sign(message); + final String url = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/" + broadcastOptions.getItemType().getValue() + "/"; + HttpURLConnection connection = null; + DataOutputStream outputStream = null; + BufferedReader r = null; + try { + connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + if (content != null) { + connection.setRequestProperty("Content-Length", "" + content.getBytes().length); + } + connection.setUseCaches(false); + connection.setDoOutput(true); + outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.writeBytes(content); + outputStream.flush(); + final int responseCode = connection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + Log.d(TAG, responseCode + ": " + content + ": " + cookie); + return new DirectThreadBroadcastResponse(responseCode, null); + } + r = new BufferedReader(new InputStreamReader(connection.getInputStream())); + final StringBuilder builder = new StringBuilder(); + for (String line = r.readLine(); line != null; line = r.readLine()) { + if (builder.length() != 0) { + builder.append("\n"); + } + builder.append(line); + } + return new DirectThreadBroadcastResponse(responseCode, new JSONObject(builder.toString())); + } catch (Exception e) { + Log.e(TAG, "Error", e); + } finally { + if (r != null) { + try { + r.close(); + } catch (IOException ignored) { + } + } + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException ignored) { + } + } + if (connection != null) { + connection.disconnect(); + } + } + return null; + } + + @Override + protected void onPostExecute(final DirectThreadBroadcastResponse result) { + if (listener != null) { + listener.onTaskComplete(result); + } + } + + public void setOnTaskCompleteListener(final OnBroadcastCompleteListener listener) { + if (listener != null) { + this.listener = listener; + } + } + + public interface OnBroadcastCompleteListener { + void onTaskComplete(DirectThreadBroadcastResponse response); + } + + public enum ItemType { + TEXT("text"), + IMAGE("configure_photo"); + + private final String value; + + ItemType(final String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public static abstract class BroadcastOptions { + private final ItemType itemType; + + public BroadcastOptions(final ItemType itemType) { + this.itemType = itemType; + } + + public ItemType getItemType() { + return itemType; + } + + abstract Map getFormMap(); + } + + public static class TextBroadcastOptions extends BroadcastOptions { + private final String text; + + public TextBroadcastOptions(String text) throws UnsupportedEncodingException { + super(ItemType.TEXT); + this.text = URLEncoder.encode(text, "UTF-8") + .replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'") + .replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~"); + } + + @Override + Map getFormMap() { + return Collections.singletonMap("text", text); + } + } + + public static class ImageBroadcastOptions extends BroadcastOptions { + final boolean allowFullAspectRatio; + final String uploadId; + + public ImageBroadcastOptions(final boolean allowFullAspectRatio, final String uploadId) { + super(ItemType.IMAGE); + this.allowFullAspectRatio = allowFullAspectRatio; + this.uploadId = uploadId; + } + + @Override + Map getFormMap() { + final Map form = new HashMap<>(); + form.put("allow_full_aspect_ratio", String.valueOf(allowFullAspectRatio)); + form.put("upload_id", uploadId); + return form; + } + } + + public static class DirectThreadBroadcastResponse { + private int responseCode; + private JSONObject response; + + public DirectThreadBroadcastResponse(int responseCode, JSONObject response) { + this.responseCode = responseCode; + this.response = response; + } + + public int getResponseCode() { + return responseCode; + } + + public JSONObject getResponse() { + return response; + } + } +} diff --git a/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java b/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java new file mode 100644 index 00000000..c7006fc2 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java @@ -0,0 +1,76 @@ +package awais.instagrabber.models; + +import java.io.InputStream; + +public class ImageUploadOptions { + private InputStream inputStream; + private long contentLength; + private boolean isSidecar; + private String waterfallId; + + public static class Builder { + private InputStream inputStream; + private long contentLength; + private boolean isSidecar; + private String waterfallId; + + public Builder(final InputStream inputStream, final long contentLength) { + this.inputStream = inputStream; + this.contentLength = contentLength; + } + + public Builder setInputStream(final InputStream inputStream) { + this.inputStream = inputStream; + return this; + } + + public Builder setContentLength(final long contentLength) { + this.contentLength = contentLength; + return this; + } + + public Builder setIsSidecar(final boolean isSidecar) { + this.isSidecar = isSidecar; + return this; + } + + public Builder setWaterfallId(final String waterfallId) { + this.waterfallId = waterfallId; + return this; + } + + public ImageUploadOptions build() { + return new ImageUploadOptions(inputStream, contentLength, isSidecar, waterfallId); + } + } + + public static Builder builder(final InputStream inputStream, final long contentLength) { + return new Builder(inputStream, contentLength); + } + + private ImageUploadOptions(final InputStream inputStream, + final long contentLength, + final boolean isSidecar, + final String waterfallId) { + this.inputStream = inputStream; + this.contentLength = contentLength; + this.isSidecar = isSidecar; + this.waterfallId = waterfallId; + } + + public InputStream getInputStream() { + return inputStream; + } + + public long getContentLength() { + return contentLength; + } + + public boolean isSidecar() { + return isSidecar; + } + + public String getWaterfallId() { + return waterfallId; + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index ee218b3d..033b3f26 100755 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -57,6 +57,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.regex.Pattern; import javax.crypto.Mac; @@ -101,6 +102,7 @@ import static awais.instagrabber.utils.Constants.FOLDER_PATH; import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; public final class Utils { + private static final String TAG = "Utils"; public static LogCollector logCollector; public static SettingsHelper settingsHelper; public static DataBox dataBox; @@ -1179,19 +1181,19 @@ public final class Utils { public static String sign(final String message) { try { - Mac hasher = Mac.getInstance("HmacSHA256"); + final Mac hasher = Mac.getInstance("HmacSHA256"); hasher.init(new SecretKeySpec(Constants.SIGNATURE_KEY.getBytes(), "HmacSHA256")); byte[] hash = hasher.doFinal(message.getBytes()); - StringBuffer hexString = new StringBuffer(); - for (int i = 0; i < hash.length; i++) { - String hex = Integer.toHexString(0xff & hash[i]); + final StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + final String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) hexString.append('0'); hexString.append(hex); } return "ig_sig_key_version="+Constants.SIGNATURE_VERSION+"&signed_body=" + hexString.toString() + "." + message; } - catch (Throwable e) { - Log.e("austin_debug", "sign: ", e); + catch (Exception e) { + Log.e(TAG, "Error signing", e); return null; } } @@ -1360,4 +1362,13 @@ public final class Utils { return null; } + + public static void setConnectionHeaders(final HttpURLConnection connection, final Map headers) { + if (connection == null || headers == null || headers.isEmpty()) { + return; + } + for (Map.Entry header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + } } \ No newline at end of file From 30695ae02f23435d223c54ed679f272da5d72f49 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Mon, 17 Aug 2020 22:49:51 +0900 Subject: [PATCH 16/21] update direct messages This commit sets up the base for future enhancements in direct messages. The changes are: 1. Introducing fragments and navigation using androidx.navigation.fragment.NavHostFragment. 2. Using ListAdapter and LiveData for the RecyclerViews. 3. Fixes a bug where a single direct message was lost when querying older messages. --- app/build.gradle | 7 +- app/src/main/AndroidManifest.xml | 11 +- .../activities/DirectMessagesActivity.java | 73 ++++ .../awais/instagrabber/activities/Main.java | 36 +- .../directmessages/DirectMessages.java | 119 ----- .../adapters/DirectMessageInboxAdapter.java | 54 +++ .../adapters/DirectMessagesAdapter.java | 131 ------ .../adapters/MessageItemsAdapter.java | 406 ++---------------- .../DirectMessageInboxItemViewHolder.java | 107 +++++ .../viewholder/DirectMessageViewHolder.java | 43 -- .../DirectMessageViewHolder.java | 383 +++++++++++++++++ .../directmessages/TextMessageViewHolder.java | 91 ---- ...a => DirectMessageInboxThreadFetcher.java} | 34 +- .../DirectMessageInboxFragment.java | 149 +++++++ .../DirectMessageThreadFragment.java} | 300 +++++++------ .../instagrabber/models/ProfileModel.java | 4 + .../direct_messages/InboxThreadModel.java | 18 +- .../models/enums/UserInboxDirection.java | 14 +- .../java/awais/instagrabber/utils/Utils.java | 22 + .../res/layout/activity_direct_messages.xml | 54 +++ .../layout/fragment_direct_messages_inbox.xml | 10 + ...ml => fragment_direct_messages_thread.xml} | 19 +- app/src/main/res/layout/item_message_item.xml | 3 +- .../navigation/direct_messages_nav_graph.xml | 26 ++ app/src/main/res/values/strings.xml | 2 +- build.gradle | 3 + 26 files changed, 1164 insertions(+), 955 deletions(-) create mode 100644 app/src/main/java/awais/instagrabber/activities/DirectMessagesActivity.java delete mode 100644 app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessages.java create mode 100644 app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java delete mode 100755 app/src/main/java/awais/instagrabber/adapters/DirectMessagesAdapter.java create mode 100644 app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java delete mode 100755 app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageViewHolder.java create mode 100644 app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageViewHolder.java delete mode 100755 app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/TextMessageViewHolder.java rename app/src/main/java/awais/instagrabber/asyncs/direct_messages/{UserInboxFetcher.java => DirectMessageInboxThreadFetcher.java} (67%) mode change 100755 => 100644 create mode 100644 app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java rename app/src/main/java/awais/instagrabber/{activities/directmessages/DirectMessageThread.java => fragments/directmessages/DirectMessageThreadFragment.java} (51%) create mode 100644 app/src/main/res/layout/activity_direct_messages.xml create mode 100644 app/src/main/res/layout/fragment_direct_messages_inbox.xml rename app/src/main/res/layout/{activity_dms.xml => fragment_direct_messages_thread.xml} (81%) mode change 100755 => 100644 create mode 100644 app/src/main/res/navigation/direct_messages_nav_graph.xml 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" + } } From 1fe721010d0938693e8136f07cb67fad4843bad6 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Mon, 17 Aug 2020 23:06:51 +0900 Subject: [PATCH 17/21] Fix names of the gen. sources due to file renames --- .../awais/instagrabber/activities/DirectMessagesActivity.java | 4 ++-- .../fragments/directmessages/DirectMessageInboxFragment.java | 2 +- .../fragments/directmessages/DirectMessageThreadFragment.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/activities/DirectMessagesActivity.java b/app/src/main/java/awais/instagrabber/activities/DirectMessagesActivity.java index c368cfb9..a3435d07 100644 --- a/app/src/main/java/awais/instagrabber/activities/DirectMessagesActivity.java +++ b/app/src/main/java/awais/instagrabber/activities/DirectMessagesActivity.java @@ -15,7 +15,7 @@ import androidx.navigation.ui.NavigationUI; import awais.instagrabber.R; import awais.instagrabber.databinding.ActivityDirectMessagesBinding; -import awais.instagrabber.fragments.directmessages.DirectMessagesThreadFragmentArgs; +import awais.instagrabber.fragments.directmessages.DirectMessageThreadFragmentArgs; public class DirectMessagesActivity extends BaseLanguageActivity implements NavController.OnDestinationChangedListener { @@ -51,7 +51,7 @@ public class DirectMessagesActivity extends BaseLanguageActivity implements NavC if (arguments == null) { return; } - final String title = DirectMessagesThreadFragmentArgs.fromBundle(arguments).getTitle(); + final String title = DirectMessageThreadFragmentArgs.fromBundle(arguments).getTitle(); setToolbarTitle(title); return; } diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java index 9e9de547..9dfbf68a 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java @@ -96,7 +96,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh layoutManager = new LinearLayoutManager(requireContext()); inboxList.setLayoutManager(layoutManager); final DirectMessageInboxAdapter inboxAdapter = new DirectMessageInboxAdapter(inboxThreadModel -> { - final NavDirections action = DirectMessagesInboxFragmentDirections.actionDMInboxFragmentToDMThreadFragment(inboxThreadModel.getThreadId(), inboxThreadModel.getThreadTitle()); + final NavDirections action = DirectMessageInboxFragmentDirections.actionDMInboxFragmentToDMThreadFragment(inboxThreadModel.getThreadId(), inboxThreadModel.getThreadTitle()); NavHostFragment.findNavController(this).navigate(action); }); inboxList.setAdapter(inboxAdapter); 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 1e6b37f2..87d6f6da 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -160,7 +160,7 @@ public class DirectMessageThreadFragment extends Fragment { if (getArguments() == null) { return root; } - threadId = DirectMessagesThreadFragmentArgs.fromBundle(getArguments()).getThreadId(); + threadId = DirectMessageThreadFragmentArgs.fromBundle(getArguments()).getThreadId(); binding.swipeRefreshLayout.setEnabled(false); messageList = binding.messageList; messageList.setHasFixedSize(true); From e2d0ee443b8a338511dd20c0fc72349c51e1094f Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Mon, 17 Aug 2020 13:53:54 -0400 Subject: [PATCH 18/21] Update .gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 8fe345fd..013266e7 100755 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,4 @@ /captures .externalNativeBuild .cxx -app/release/app-release.apk -app/release/output.json +app/release From 31a5c437f194a1ee3dc836e46ef20f46b87a3595 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Tue, 18 Aug 2020 22:20:00 +0900 Subject: [PATCH 19/21] enable debug for resource leak (when close is not called) --- app/src/main/java/awais/instagrabber/InstaApp.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/awais/instagrabber/InstaApp.java b/app/src/main/java/awais/instagrabber/InstaApp.java index 9bc2d396..05cdce20 100755 --- a/app/src/main/java/awais/instagrabber/InstaApp.java +++ b/app/src/main/java/awais/instagrabber/InstaApp.java @@ -2,6 +2,7 @@ package awais.instagrabber; import android.content.ClipboardManager; import android.content.Context; +import android.util.Log; import androidx.core.app.NotificationManagerCompat; import androidx.multidex.MultiDexApplication; @@ -32,6 +33,16 @@ public final class InstaApp extends MultiDexApplication { public void onCreate() { super.onCreate(); + if (BuildConfig.DEBUG) { + try { + Class.forName("dalvik.system.CloseGuard") + .getMethod("setEnabled", boolean.class) + .invoke(null, true); + } catch (Exception e) { + Log.e("InstaApp", "Error", e); + } + } + if (!BuildConfig.DEBUG) CrashReporter.get(this).start(); logCollector = new LogCollector(this); From 4a73aa1179388e341b59e27edfb11a0116b214d1 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Tue, 18 Aug 2020 22:22:16 +0900 Subject: [PATCH 20/21] allow uploading any type of image Tested all formats (except MNG) from https://www.w3.org/People/mimasa/test/imgformat/ --- .../instagrabber/asyncs/ImageUploader.java | 50 +++++++++------ .../DirectMessageThreadFragment.java | 63 +++++++++---------- .../models/ImageUploadOptions.java | 42 +++++-------- 3 files changed, 74 insertions(+), 81 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java b/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java index 41454158..f7d97cd1 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java @@ -1,11 +1,17 @@ package awais.instagrabber.asyncs; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; import org.json.JSONObject; +import java.io.BufferedOutputStream; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -35,13 +41,24 @@ public class ImageUploader extends AsyncTask headers = new HashMap<>(); final String uploadId = String.valueOf(new Date().getTime()); final long random = LOWER + new Random().nextLong() * (UPPER - LOWER + 1); final String name = String.format("%s_0_%s", uploadId, random); - final String contentLength = String.valueOf(options.getContentLength()); final String waterfallId = options.getWaterfallId() != null ? options.getWaterfallId() : UUID.randomUUID().toString(); headers.put("X-Entity-Type", "image/jpeg"); headers.put("Offset", "0"); @@ -58,20 +75,15 @@ public class ImageUploader extends AsyncTask { + if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) { + Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + if (response != null && response.getResponse() != null) { + Log.e(TAG, response.getResponse().toString()); + } return; } - final long contentLength = fileDescriptor.getStatSize(); - 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(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - if (response != null && response.getResponse() != null) { - Log.e(TAG, response.getResponse().toString()); - } - return; - } - final JSONObject responseJson = response.getResponse(); - try { - final String uploadId = responseJson.getString("upload_id"); - // Broadcast - 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); - } - }); - final ImageUploadOptions options = ImageUploadOptions.builder(inputStream, contentLength).build(); - imageUploader.execute(options); - } catch (FileNotFoundException e) { - Log.e(TAG, "Error opening InputStream", e); - } + final JSONObject responseJson = response.getResponse(); + try { + final String uploadId = responseJson.getString("upload_id"); + // Broadcast + 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); + } + }); + final ImageUploadOptions options = ImageUploadOptions.builder(file).build(); + imageUploader.execute(options); } private void broadcast(final DirectThreadBroadcaster.BroadcastOptions broadcastOptions, final DirectThreadBroadcaster.OnBroadcastCompleteListener listener) { diff --git a/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java b/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java index c7006fc2..30918914 100644 --- a/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java +++ b/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java @@ -1,31 +1,23 @@ package awais.instagrabber.models; -import java.io.InputStream; +import java.io.File; public class ImageUploadOptions { - private InputStream inputStream; - private long contentLength; + private final File file; private boolean isSidecar; private String waterfallId; public static class Builder { - private InputStream inputStream; - private long contentLength; + private File file; private boolean isSidecar; private String waterfallId; - public Builder(final InputStream inputStream, final long contentLength) { - this.inputStream = inputStream; - this.contentLength = contentLength; + public Builder(final File file) { + this.file = file; } - public Builder setInputStream(final InputStream inputStream) { - this.inputStream = inputStream; - return this; - } - - public Builder setContentLength(final long contentLength) { - this.contentLength = contentLength; + public Builder setFile(final File file) { + this.file = file; return this; } @@ -40,30 +32,24 @@ public class ImageUploadOptions { } public ImageUploadOptions build() { - return new ImageUploadOptions(inputStream, contentLength, isSidecar, waterfallId); + return new ImageUploadOptions(file, isSidecar, waterfallId); } } - public static Builder builder(final InputStream inputStream, final long contentLength) { - return new Builder(inputStream, contentLength); + public static Builder builder(final File file) { + return new Builder(file); } - private ImageUploadOptions(final InputStream inputStream, - final long contentLength, + private ImageUploadOptions(final File file, final boolean isSidecar, final String waterfallId) { - this.inputStream = inputStream; - this.contentLength = contentLength; + this.file = file; this.isSidecar = isSidecar; this.waterfallId = waterfallId; } - public InputStream getInputStream() { - return inputStream; - } - - public long getContentLength() { - return contentLength; + public File getFile() { + return file; } public boolean isSidecar() { From f9ef6971117b209216ac92de652f0adaa6cfb794 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Wed, 19 Aug 2020 00:15:13 +0900 Subject: [PATCH 21/21] Fix error when choosing image from Documents manager Cannot rely on Uri.getPath to return absolute path to create File object. Instead get the inputstream and create Bitmap first. --- .../instagrabber/asyncs/ImageUploader.java | 7 +-- .../DirectMessageThreadFragment.java | 61 ++++++++++--------- .../models/ImageUploadOptions.java | 26 ++++---- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java b/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java index f7d97cd1..e4cdaa6e 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java @@ -1,7 +1,6 @@ package awais.instagrabber.asyncs; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; @@ -10,8 +9,6 @@ import org.json.JSONObject; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -44,9 +41,7 @@ public class ImageUploader extends AsyncTask { - if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) { - Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - if (response != null && response.getResponse() != null) { - Log.e(TAG, response.getResponse().toString()); + try(InputStream inputStream = requireContext().getContentResolver().openInputStream(imageUri)) { + final Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + // Upload Image + final ImageUploader imageUploader = new ImageUploader(); + imageUploader.setOnTaskCompleteListener(response -> { + if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) { + Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + if (response != null && response.getResponse() != null) { + Log.e(TAG, response.getResponse().toString()); + } + return; } - return; - } - final JSONObject responseJson = response.getResponse(); - try { - final String uploadId = responseJson.getString("upload_id"); - // Broadcast - 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); - } - }); - final ImageUploadOptions options = ImageUploadOptions.builder(file).build(); - imageUploader.execute(options); + final JSONObject responseJson = response.getResponse(); + try { + final String uploadId = responseJson.getString("upload_id"); + // Broadcast + 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); + } + }); + final ImageUploadOptions options = ImageUploadOptions.builder(bitmap).build(); + imageUploader.execute(options); + } + catch (IOException e) { + Log.e(TAG, "Error opening file", e); + } } private void broadcast(final DirectThreadBroadcaster.BroadcastOptions broadcastOptions, final DirectThreadBroadcaster.OnBroadcastCompleteListener listener) { diff --git a/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java b/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java index 30918914..94ce93d1 100644 --- a/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java +++ b/app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java @@ -1,23 +1,23 @@ package awais.instagrabber.models; -import java.io.File; +import android.graphics.Bitmap; public class ImageUploadOptions { - private final File file; + private final Bitmap bitmap; private boolean isSidecar; private String waterfallId; public static class Builder { - private File file; + private Bitmap bitmap; private boolean isSidecar; private String waterfallId; - public Builder(final File file) { - this.file = file; + public Builder(final Bitmap bitmap) { + this.bitmap = bitmap; } - public Builder setFile(final File file) { - this.file = file; + public Builder setBitmap(final Bitmap bitmap) { + this.bitmap = bitmap; return this; } @@ -32,24 +32,24 @@ public class ImageUploadOptions { } public ImageUploadOptions build() { - return new ImageUploadOptions(file, isSidecar, waterfallId); + return new ImageUploadOptions(bitmap, isSidecar, waterfallId); } } - public static Builder builder(final File file) { + public static Builder builder(final Bitmap file) { return new Builder(file); } - private ImageUploadOptions(final File file, + private ImageUploadOptions(final Bitmap bitmap, final boolean isSidecar, final String waterfallId) { - this.file = file; + this.bitmap = bitmap; this.isSidecar = isSidecar; this.waterfallId = waterfallId; } - public File getFile() { - return file; + public Bitmap getBitmap() { + return bitmap; } public boolean isSidecar() {