Browse Source

Merge branch 'master' into task/separate-dm-type-views

legacy
Ammar Githam 4 years ago
parent
commit
052402a974
  1. 4
      app/build.gradle
  2. 66
      app/src/main/java/awais/instagrabber/activities/Main.java
  3. 51
      app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java
  4. 61
      app/src/main/java/awais/instagrabber/activities/StoryViewer.java
  5. 113
      app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java
  6. 2
      app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
  7. 24
      app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java
  8. 170
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
  9. 4
      app/src/main/java/awais/instagrabber/utils/UpdateChecker.java
  10. 4
      app/src/main/java/awais/instagrabber/utils/Utils.java
  11. 7
      app/src/main/res/values/strings.xml

4
app/build.gradle

@ -10,8 +10,8 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 45
versionName '17.9'
versionCode 46
versionName '18.0'
multiDexEnabled true multiDexEnabled true

66
app/src/main/java/awais/instagrabber/activities/Main.java

@ -1,5 +1,7 @@
package awais.instagrabber.activities; package awais.instagrabber.activities;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -7,8 +9,10 @@ import android.content.res.Resources;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle; import android.os.PersistableBundle;
import android.provider.BaseColumns; import android.provider.BaseColumns;
import android.text.TextUtils;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -20,10 +24,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.core.app.NotificationCompat;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Stack; import java.util.Stack;
import awais.instagrabber.BuildConfig; import awais.instagrabber.BuildConfig;
@ -31,6 +37,7 @@ import awais.instagrabber.MainHelper;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.HighlightsAdapter; import awais.instagrabber.adapters.HighlightsAdapter;
import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.GetActivityAsyncTask;
import awais.instagrabber.asyncs.SuggestionsFetcher; import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.asyncs.UsernameFetcher; import awais.instagrabber.asyncs.UsernameFetcher;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher; import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
@ -58,6 +65,8 @@ import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.FlavorTown; import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.CHANNEL_ID;
import static awais.instagrabber.utils.Utils.notificationManager;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
public final class Main extends BaseLanguageActivity { public final class Main extends BaseLanguageActivity {
@ -97,7 +106,7 @@ public final class Main extends BaseLanguageActivity {
public SearchView searchView; public SearchView searchView;
public MenuItem downloadAction, settingsAction, dmsAction, notifAction; public MenuItem downloadAction, settingsAction, dmsAction, notifAction;
public StoryModel[] storyModels; public StoryModel[] storyModels;
public String userQuery = null;
public String userQuery = null, cookie, uid = null;
public MainHelper mainHelper; public MainHelper mainHelper;
public ProfileModel profileModel; public ProfileModel profileModel;
public HashtagModel hashtagModel; public HashtagModel hashtagModel;
@ -107,6 +116,7 @@ public final class Main extends BaseLanguageActivity {
private DialogInterface.OnClickListener profileDialogListener; private DialogInterface.OnClickListener profileDialogListener;
private Stack<String> queriesStack; private Stack<String> queriesStack;
private DataBox.CookieModel cookieModel; private DataBox.CookieModel cookieModel;
private Runnable runnable;
@Override @Override
protected void onCreate(@Nullable final Bundle bundle) { protected void onCreate(@Nullable final Bundle bundle) {
@ -117,8 +127,8 @@ public final class Main extends BaseLanguageActivity {
FlavorTown.updateCheck(this); FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this); FlavorTown.changelogCheck(this);
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String uid = Utils.getUserIdFromCookie(cookie);
cookie = settingsHelper.getString(Constants.COOKIE);
uid = Utils.getUserIdFromCookie(cookie);
Utils.setupCookies(cookie); Utils.setupCookies(cookie);
MainHelper.stopCurrentExecutor(); MainHelper.stopCurrentExecutor();
@ -246,6 +256,56 @@ public final class Main extends BaseLanguageActivity {
mainHelper.onRefresh(); mainHelper.onRefresh();
mainHelper.onIntent(getIntent()); mainHelper.onIntent(getIntent());
final Handler handler = new Handler();
runnable = () -> {
final GetActivityAsyncTask activityAsyncTask = new GetActivityAsyncTask(uid, cookie, result -> {
if (result == null) {
if (!Utils.isEmpty(cookie)) {
Toast.makeText(Main.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
return;
}
if (notificationManager == null) {
return;
}
final List<String> list = new ArrayList<>();
if (result.getRelationshipsCount() != 0) {
list.add(getString(R.string.activity_count_relationship, result.getRelationshipsCount()));
}
if (result.getUserTagsCount() != 0) {
list.add(getString(R.string.activity_count_usertags, result.getUserTagsCount()));
}
if (result.getCommentsCount() != 0) {
list.add(getString(R.string.activity_count_comments, result.getCommentsCount()));
}
if (result.getCommentLikesCount() != 0) {
list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikesCount()));
}
if (result.getLikesCount() != 0) {
list.add(getString(R.string.activity_count_likes, result.getLikesCount()));
}
if (list.isEmpty()) {
return;
}
final String join = TextUtils.join(", ", list);
final String notificationString = getString(R.string.activity_count_prefix) + " " + join + ".";
final Intent intent = new Intent(getApplicationContext(), NotificationsViewer.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Notification notification = new NotificationCompat.Builder(Main.this, CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setSmallIcon(R.drawable.ic_notif)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setContentText(notificationString)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 1738, intent, PendingIntent.FLAG_UPDATE_CURRENT))
.build();
notificationManager.cancel(1800000000);
notificationManager.notify(1800000000, notification);
});
activityAsyncTask.execute();
handler.postDelayed(runnable, 60000);
};
handler.postDelayed(runnable, 200);
} }
private void downloadSelectedItems() { private void downloadSelectedItems() {

51
app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java

@ -35,8 +35,9 @@ import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.notificationManager;
public final class NotificationsViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener { public final class NotificationsViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
private NotificationsAdapter notificationsAdapter;
private NotificationModel notificationModel; private NotificationModel notificationModel;
private ActivityNotificationBinding notificationsBinding; private ActivityNotificationBinding notificationsBinding;
private ArrayAdapter<String> commmentDialogAdapter; private ArrayAdapter<String> commmentDialogAdapter;
@ -47,26 +48,18 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
@Override @Override
protected void onCreate(@Nullable final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
notificationManager.cancel(1800000000);
if (Utils.isEmpty(cookie)) {
Toast.makeText(this, R.string.activity_notloggedin, Toast.LENGTH_SHORT).show();
}
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
notificationsBinding = ActivityNotificationBinding.inflate(getLayoutInflater()); notificationsBinding = ActivityNotificationBinding.inflate(getLayoutInflater());
setContentView(notificationsBinding.getRoot()); setContentView(notificationsBinding.getRoot());
notificationsBinding.swipeRefreshLayout.setOnRefreshListener(this); notificationsBinding.swipeRefreshLayout.setOnRefreshListener(this);
notificationsBinding.swipeRefreshLayout.setRefreshing(true);
resources = getResources();
setSupportActionBar(notificationsBinding.toolbar.toolbar); setSupportActionBar(notificationsBinding.toolbar.toolbar);
notificationsBinding.toolbar.toolbar.setTitle(R.string.action_notif); notificationsBinding.toolbar.toolbar.setTitle(R.string.action_notif);
resources = getResources();
new NotificationsFetcher(new FetchListener<NotificationModel[]>() {
@Override
public void onResult(final NotificationModel[] notificationModels) {
notificationsAdapter = new NotificationsAdapter(notificationModels, clickListener, mentionClickListener);
notificationsBinding.rvComments.setAdapter(notificationsAdapter);
notificationsBinding.swipeRefreshLayout.setRefreshing(false);
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
onRefresh();
} }
@Override @Override
@ -75,11 +68,9 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
new NotificationsFetcher(new FetchListener<NotificationModel[]>() { new NotificationsFetcher(new FetchListener<NotificationModel[]>() {
@Override @Override
public void onResult(final NotificationModel[] notificationModels) { public void onResult(final NotificationModel[] notificationModels) {
notificationsBinding.rvComments.setAdapter(new NotificationsAdapter(notificationModels, clickListener, mentionClickListener));
notificationsBinding.swipeRefreshLayout.setRefreshing(false); notificationsBinding.swipeRefreshLayout.setRefreshing(false);
notificationsAdapter = new NotificationsAdapter(notificationModels, clickListener, mentionClickListener);
notificationsBinding.rvComments.setAdapter(notificationsAdapter);
new SeenAction().execute();
} }
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
@ -150,7 +141,8 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
urlConnection.setUseCaches(false); urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT); urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
urlConnection.setRequestProperty("x-csrftoken", urlConnection.setRequestProperty("x-csrftoken",
Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);urlConnection.connect();
Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true; ok = true;
} }
@ -169,4 +161,23 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
} }
} }
class SeenAction extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
try {
final HttpURLConnection urlConnection =
(HttpURLConnection) new URL("https://www.instagram.com/web/activity/mark_checked/").openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
urlConnection.setRequestProperty("x-csrftoken",
Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
urlConnection.connect();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "seen: " + ex);
}
return null;
}
}
} }

61
app/src/main/java/awais/instagrabber/activities/StoryViewer.java

@ -56,6 +56,7 @@ import awais.instagrabber.BuildConfig;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.StoriesAdapter; import awais.instagrabber.adapters.StoriesAdapter;
import awais.instagrabber.asyncs.DownloadAsync; import awais.instagrabber.asyncs.DownloadAsync;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher; import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
import awais.instagrabber.customviews.helpers.SwipeGestureListener; import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.ActivityStoryViewerBinding; import awais.instagrabber.databinding.ActivityStoryViewerBinding;
@ -723,8 +724,6 @@ final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStory
} }
class CommentAction extends AsyncTask<String, Void, Void> { class CommentAction extends AsyncTask<String, Void, Void> {
boolean ok = false;
protected Void doInBackground(String... rawAction) { protected Void doInBackground(String... rawAction) {
final String action = rawAction[0]; final String action = rawAction[0];
final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/"; final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/";
@ -747,55 +746,27 @@ final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStory
wr.close(); wr.close();
urlConnection.connect(); urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
try {
final String threadid = new JSONObject(Utils.readFromConnection(urlConnection)).getString("thread_id");
final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/reel_share/";
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(action, "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
+"\",\"media_id\":\"" + currentStory.getStoryMediaId()
+"\",\"reel_id\":\"" + currentStory.getUserId()
+"\",\"thread_ids\":\"["+threadid
+"]\",\"action\":\"send_item\",\"entry\":\"reel\"}");
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();
if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection2.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "reply (B): " + ex);
}
final String threadid = new JSONObject(Utils.readFromConnection(urlConnection)).getString("thread_id");
final DirectThreadBroadcaster.StoryReplyBroadcastOptions options =
new DirectThreadBroadcaster.StoryReplyBroadcastOptions(
action,
currentStory.getStoryMediaId(),
currentStory.getUserId()
);
final DirectThreadBroadcaster broadcast = new DirectThreadBroadcaster(threadid);
broadcast.setOnTaskCompleteListener(result -> {
Toast.makeText(getApplicationContext(),
result != null ? R.string.answered_story : R.string.downloader_unknown_error,
Toast.LENGTH_SHORT).show();
});
broadcast.execute(options);
} }
urlConnection.disconnect(); urlConnection.disconnect();
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e("austin_debug", "reply (CT): " + ex); Log.e("austin_debug", "reply (CT): " + ex);
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
} }
return null; return null;
} }
@Override
protected void onPostExecute(Void result) {
Toast.makeText(getApplicationContext(),
ok ? R.string.answered_story : R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
} }
} }

113
app/src/main/java/awais/instagrabber/asyncs/GetActivityAsyncTask.java

@ -0,0 +1,113 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
public class GetActivityAsyncTask extends AsyncTask<Void, Void, GetActivityAsyncTask.NotificationCounts> {
private static final String TAG = "GetActivityAsyncTask";
private String uid;
private String cookie;
private OnTaskCompleteListener onTaskCompleteListener;
public GetActivityAsyncTask(final String uid, final String cookie, final OnTaskCompleteListener onTaskCompleteListener) {
this.uid = uid;
this.cookie = cookie;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected NotificationCounts doInBackground(Void... voids) {
if (Utils.isEmpty(cookie)) {
return null;
}
final String url = "https://www.instagram.com/graphql/query/?query_hash=0f318e8cfff9cc9ef09f88479ff571fb"
+ "&variables={\"id\":\"" + uid + "\"}";
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
urlConnection.connect();
if (urlConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return null;
}
final JSONObject data = new JSONObject(Utils.readFromConnection(urlConnection)).getJSONObject("data")
.getJSONObject("user").getJSONObject("edge_activity_count").getJSONArray("edges").getJSONObject(0)
.getJSONObject("node");
return new NotificationCounts(
data.getInt("relationships"),
data.getInt("usertags"),
data.getInt("comments"),
data.getInt("comment_likes"),
data.getInt("likes")
);
} catch (Throwable ex) {
Log.e(TAG, "Error", ex);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
@Override
protected void onPostExecute(final NotificationCounts result) {
if (onTaskCompleteListener == null) {
return;
}
onTaskCompleteListener.onTaskComplete(result);
}
public static class NotificationCounts {
private int relationshipsCount;
private int userTagsCount;
private int commentsCount;
private int commentLikesCount;
private int likesCount;
public NotificationCounts(final int relationshipsCount,
final int userTagsCount,
final int commentsCount,
final int commentLikesCount,
final int likesCount) {
this.relationshipsCount = relationshipsCount;
this.userTagsCount = userTagsCount;
this.commentsCount = commentsCount;
this.commentLikesCount = commentLikesCount;
this.likesCount = likesCount;
}
public int getRelationshipsCount() {
return relationshipsCount;
}
public int getUserTagsCount() {
return userTagsCount;
}
public int getCommentsCount() {
return commentsCount;
}
public int getCommentLikesCount() {
return commentLikesCount;
}
public int getLikesCount() {
return likesCount;
}
}
public interface OnTaskCompleteListener {
void onTaskComplete(final NotificationCounts result);
}
}

2
app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java

@ -19,6 +19,7 @@ import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector; import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class NotificationsFetcher extends AsyncTask<Void, Void, NotificationModel[]> { public final class NotificationsFetcher extends AsyncTask<Void, Void, NotificationModel[]> {
private final FetchListener<NotificationModel[]> fetchListener; private final FetchListener<NotificationModel[]> fetchListener;
@ -31,6 +32,7 @@ public final class NotificationsFetcher extends AsyncTask<Void, Void, Notificati
protected NotificationModel[] doInBackground(final Void... voids) { protected NotificationModel[] doInBackground(final Void... voids) {
NotificationModel[] result = null; NotificationModel[] result = null;
final String url = "https://www.instagram.com/accounts/activity/?__a=1"; final String url = "https://www.instagram.com/accounts/activity/?__a=1";
Utils.setupCookies(settingsHelper.getString(Constants.COOKIE));
try { try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();

24
app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java

@ -126,6 +126,7 @@ public class DirectThreadBroadcaster extends AsyncTask<DirectThreadBroadcaster.B
public enum ItemType { public enum ItemType {
TEXT("text"), TEXT("text"),
REACTION("reaction"), REACTION("reaction"),
REELSHARE("reel_share"),
IMAGE("configure_photo"); IMAGE("configure_photo");
private final String value; private final String value;
@ -189,6 +190,29 @@ public class DirectThreadBroadcaster extends AsyncTask<DirectThreadBroadcaster.B
} }
} }
public static class StoryReplyBroadcastOptions extends BroadcastOptions {
private final String text, mediaId, reelId;
public StoryReplyBroadcastOptions(String text, String mediaId, String reelId) throws UnsupportedEncodingException {
super(ItemType.REELSHARE);
this.text = URLEncoder.encode(text, "UTF-8")
.replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'")
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~");
this.mediaId = mediaId;
this.reelId = reelId; // or user id, usually same
}
@Override
Map<String, String> getFormMap() {
final Map<String, String> form = new HashMap<>();
form.put("text", text);
form.put("media_id", mediaId);
form.put("reel_id", reelId);
form.put("entry", "reel");
return form;
}
}
public static class ImageBroadcastOptions extends BroadcastOptions { public static class ImageBroadcastOptions extends BroadcastOptions {
final boolean allowFullAspectRatio; final boolean allowFullAspectRatio;
final String uploadId; final String uploadId;

170
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java

@ -31,15 +31,20 @@ import androidx.recyclerview.widget.RecyclerView;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.activities.PostViewer; import awais.instagrabber.activities.PostViewer;
@ -78,7 +83,7 @@ public class DirectMessageThreadFragment extends Fragment {
private DirectItemModelListViewModel listViewModel; private DirectItemModelListViewModel listViewModel;
private DirectItemModel directItemModel; private DirectItemModel directItemModel;
private RecyclerView messageList; private RecyclerView messageList;
private boolean hasSentSomething;
private boolean hasSentSomething, hasDeletedSomething;
private boolean hasOlder = true; private boolean hasOlder = true;
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel();
@ -135,15 +140,16 @@ public class DirectMessageThreadFragment extends Fragment {
List<DirectItemModel> list = listViewModel.getList().getValue(); List<DirectItemModel> list = listViewModel.getList().getValue();
final List<DirectItemModel> newList = Arrays.asList(result.getItems()); final List<DirectItemModel> newList = Arrays.asList(result.getItems());
list = list != null ? new LinkedList<>(list) : new LinkedList<>(); list = list != null ? new LinkedList<>(list) : new LinkedList<>();
if (hasSentSomething) {
if (hasSentSomething || hasDeletedSomething) {
list = newList; list = newList;
hasSentSomething = false;
final Handler handler = new Handler(); final Handler handler = new Handler();
handler.postDelayed(() -> {
if (hasSentSomething) handler.postDelayed(() -> {
if (messageList != null) { if (messageList != null) {
messageList.smoothScrollToPosition(0); messageList.smoothScrollToPosition(0);
} }
}, 200); }, 200);
hasSentSomething = false;
hasDeletedSomething = false;
} else { } else {
list.addAll(newList); list.addAll(newList);
} }
@ -186,67 +192,64 @@ public class DirectMessageThreadFragment extends Fragment {
})); }));
final DialogInterface.OnClickListener onDialogListener = (dialogInterface, which) -> { final DialogInterface.OnClickListener onDialogListener = (dialogInterface, which) -> {
switch (which) {
case 0:
final DirectItemType itemType = directItemModel.getItemType();
switch (itemType) {
case MEDIA_SHARE:
startActivity(new Intent(requireContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode(), false)));
break;
case LINK:
Intent linkIntent = new Intent(Intent.ACTION_VIEW);
linkIntent.setData(Uri.parse(directItemModel.getLinkModel().getLinkContext().getLinkUrl()));
startActivity(linkIntent);
break;
case TEXT:
case REEL_SHARE:
Utils.copyText(requireContext(), directItemModel.getText());
Toast.makeText(requireContext(), R.string.clipboard_copied, Toast.LENGTH_SHORT).show();
break;
case RAVEN_MEDIA:
case MEDIA:
final ProfileModel user = getUser(directItemModel.getUserId());
Utils.dmDownload(requireContext(), user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia()));
Toast.makeText(requireContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
break;
case STORY_SHARE:
if (directItemModel.getReelShare() != null) {
StoryModel sm = new StoryModel(
directItemModel.getReelShare().getReelId(),
directItemModel.getReelShare().getMedia().getVideoUrl(),
directItemModel.getReelShare().getMedia().getMediaType(),
directItemModel.getTimestamp(),
directItemModel.getReelShare().getReelOwnerName(),
String.valueOf(directItemModel.getReelShare().getReelOwnerId()),
false
);
sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl());
StoryModel[] sms = {sm};
startActivity(new Intent(requireContext(), StoryViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName())
.putExtra(Constants.EXTRAS_STORIES, sms)
);
} else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) {
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]);
}
break;
case PLACEHOLDER:
if (directItemModel.getText().toString().contains("@"))
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]);
break;
default:
Log.d("austin_debug", "unsupported type " + itemType);
}
break;
case 1:
sendText(null, directItemModel.getItemId(), directItemModel.isLiked());
break;
case 2:
if (String.valueOf(directItemModel.getUserId()).equals(myId)) {
// unsend: https://www.instagram.com/direct_v2/web/threads/340282366841710300949128288687654467119/items/29473546990090204551245070881259520/delete/
} else searchUsername(getUser(directItemModel.getUserId()).getUsername());
break;
if (which == 0) {
final DirectItemType itemType = directItemModel.getItemType();
switch (itemType) {
case MEDIA_SHARE:
startActivity(new Intent(requireContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode(), false)));
break;
case LINK:
Intent linkIntent = new Intent(Intent.ACTION_VIEW);
linkIntent.setData(Uri.parse(directItemModel.getLinkModel().getLinkContext().getLinkUrl()));
startActivity(linkIntent);
break;
case TEXT:
case REEL_SHARE:
Utils.copyText(requireContext(), directItemModel.getText());
Toast.makeText(requireContext(), R.string.clipboard_copied, Toast.LENGTH_SHORT).show();
break;
case RAVEN_MEDIA:
case MEDIA:
final ProfileModel user = getUser(directItemModel.getUserId());
Utils.dmDownload(requireContext(), user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia()));
Toast.makeText(requireContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
break;
case STORY_SHARE:
if (directItemModel.getReelShare() != null) {
StoryModel sm = new StoryModel(
directItemModel.getReelShare().getReelId(),
directItemModel.getReelShare().getMedia().getVideoUrl(),
directItemModel.getReelShare().getMedia().getMediaType(),
directItemModel.getTimestamp(),
directItemModel.getReelShare().getReelOwnerName(),
String.valueOf(directItemModel.getReelShare().getReelOwnerId()),
false
);
sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl());
StoryModel[] sms = {sm};
startActivity(new Intent(requireContext(), StoryViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName())
.putExtra(Constants.EXTRAS_STORIES, sms)
);
} else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) {
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]);
}
break;
case PLACEHOLDER:
if (directItemModel.getText().toString().contains("@"))
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]);
break;
default:
Log.d("austin_debug", "unsupported type " + itemType);
}
}
else if (which == 1) {
sendText(null, directItemModel.getItemId(), directItemModel.isLiked());
}
else if (which == 2) {
if (String.valueOf(directItemModel.getUserId()).equals(myId)) new Unsend().execute();
else searchUsername(getUser(directItemModel.getUserId()).getUsername());
} }
}; };
final View.OnClickListener onClickListener = v -> { final View.OnClickListener onClickListener = v -> {
@ -427,4 +430,41 @@ public class DirectMessageThreadFragment extends Fragment {
return list; return list;
} }
} }
class Unsend extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/"+threadId+"/items/"+directItemModel.getItemId()+"/delete/";
try {
String urlParameters = "_csrftoken=" + cookie.split("csrftoken=")[1].split(";")[0]
+"&_uuid=" + Utils.settingsHelper.getString(Constants.DEVICE_UUID);
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
hasDeletedSomething = true;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "unsend: " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (hasDeletedSomething) {
directItemModel = null;
new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}
} }

4
app/src/main/java/awais/instagrabber/utils/UpdateChecker.java

@ -33,9 +33,9 @@ public final class UpdateChecker extends AsyncTask<Void, Void, Boolean> {
conn.connect(); conn.connect();
final int responseCode = conn.getResponseCode(); final int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP) {
if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP && !BuildConfig.DEBUG) {
version = conn.getHeaderField("Location").split("/v")[1]; version = conn.getHeaderField("Location").split("/v")[1];
return Float.parseFloat(version) > Float.parseFloat(BuildConfig.VERSION_NAME);
return version != BuildConfig.VERSION_NAME;
} }
conn.disconnect(); conn.disconnect();

4
app/src/main/java/awais/instagrabber/utils/Utils.java

@ -175,8 +175,8 @@ public final class Utils {
clipString = clipString.substring((isHttps ? 22 : 21) + wwwDel); clipString = clipString.substring((isHttps ? 22 : 21) + wwwDel);
final char firstChar = clipString.charAt(0); final char firstChar = clipString.charAt(0);
if (clipString.startsWith("p/") || clipString.startsWith("reel/")) {
clipString = clipString.substring(clipString.startsWith("p/") ? 2 : 5);
if (clipString.startsWith("p/") || clipString.startsWith("reel/") || clipString.startsWith("tv/")) {
clipString = clipString.substring(clipString.startsWith("p/") ? 2 : (clipString.startsWith("tv/") ? 3 : 5));
type = IntentModelType.POST; type = IntentModelType.POST;
} else if (clipString.startsWith("explore/tags/")) { } else if (clipString.startsWith("explore/tags/")) {
clipString = clipString.substring(13); clipString = clipString.substring(13);

7
app/src/main/res/values/strings.xml

@ -222,4 +222,11 @@
<string name="license" translatable="false">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.</string> <string name="license" translatable="false">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.</string>
<string name="select_picture">Select Picture</string> <string name="select_picture">Select Picture</string>
<string name="uploading">Uploading...</string> <string name="uploading">Uploading...</string>
<string name="activity_count_prefix">You have:</string>
<string name="activity_count_relationship">%d follows</string>
<string name="activity_count_comments">%d comments</string>
<string name="activity_count_commentlikes">%d comment likes</string>
<string name="activity_count_usertags">%d usertags</string>
<string name="activity_count_likes">%d likes</string>
<string name="activity_notloggedin">You logged out before clicking this notification?!</string>
</resources> </resources>
Loading…
Cancel
Save