diff --git a/README.md b/README.md
index 5d793f41..9b7e2b56 100755
--- a/README.md
+++ b/README.md
@@ -16,10 +16,12 @@ InstaGrabber is an app that allows...
It can be used as a drop-in replacement for read functionalities of the official Instagram app, with unnecessary components stripped.
-
-
-
-
+
+
+
+
+
+
This app is originally made by [@AwaisKing](https://github.com/AwaisKing) who posted on [GitLab](https://gitlab.com/AwaisKing/instagrabber) but subsequently abandoned it. I decided to continue the app cuz why not, ~~even though it might not be that *cash money*.~~ (Also I need to learn Java.)
@@ -34,6 +36,7 @@ Not compatible with pre-16.6 versions (including alpha).
Remember to read the [wiki](https://github.com/austinhuang0131/instagrabber/wiki) for more info!
[![Open Source Love svg3](https://badges.frapsoft.com/os/v3/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/)
+[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/austinhuang0131/instagrabber)](https://snyk.io/test/github/austinhuang0131/instagrabber)
[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/austinhuang0131/instagrabber)](https://lgtm.com/projects/g/austinhuang0131/instagrabber)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 613d579d..ff0a5084 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -177,6 +177,24 @@
android:value=".activities.Main" />
+
+
+
+
+
+
+
+
+
+
{
@@ -779,8 +788,9 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
final String myId = Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE));
if (!profileId.equals(myId)) {
+ main.mainBinding.btnTagged.setVisibility(View.GONE);
+ main.mainBinding.btnSaved.setVisibility(View.GONE);
main.mainBinding.btnFollow.setVisibility(View.VISIBLE);
- main.mainBinding.btnFollow.setOnClickListener(profileActionListener);
if (profileModel.getFollowing() == true) {
main.mainBinding.btnFollow.setText(R.string.unfollow);
main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
@@ -797,43 +807,69 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
R.color.btn_pink_background, null)));
}
main.mainBinding.btnRestrict.setVisibility(View.VISIBLE);
- main.mainBinding.btnRestrict.setOnClickListener(profileActionListener);
if (profileModel.getRestricted() == true) {
main.mainBinding.btnRestrict.setText(R.string.unrestrict);
main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
- R.color.btn_blue_background, null)));
+ R.color.btn_green_background, null)));
}
else {
main.mainBinding.btnRestrict.setText(R.string.restrict);
main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
R.color.btn_orange_background, null)));
}
- main.mainBinding.btnBlock.setVisibility(View.VISIBLE);
- main.mainBinding.btnBlock.setOnClickListener(profileActionListener);
- if (profileModel.getBlocked() == true) {
- main.mainBinding.btnBlock.setText(R.string.unblock);
- main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
- R.color.btn_green_background, null)));
- }
- else {
- main.mainBinding.btnBlock.setText(R.string.block);
- main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
- R.color.btn_red_background, null)));
+ if (profileModel.isReallyPrivate()) {
+ main.mainBinding.btnBlock.setVisibility(View.VISIBLE);
+ main.mainBinding.btnSaved.setVisibility(View.GONE);
+ main.mainBinding.btnTagged.setVisibility(View.GONE);
+ if (profileModel.getBlocked() == true) {
+ main.mainBinding.btnBlock.setText(R.string.unblock);
+ main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
+ R.color.btn_green_background, null)));
+ } else {
+ main.mainBinding.btnBlock.setText(R.string.block);
+ main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
+ R.color.btn_red_background, null)));
+ }
+ } else {
+ main.mainBinding.btnBlock.setVisibility(View.GONE);
+ main.mainBinding.btnSaved.setVisibility(View.VISIBLE);
+ main.mainBinding.btnTagged.setVisibility(View.VISIBLE);
+ if (profileModel.getBlocked() == true) {
+ main.mainBinding.btnSaved.setText(R.string.unblock);
+ main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
+ R.color.btn_green_background, null)));
+ } else {
+ main.mainBinding.btnSaved.setText(R.string.block);
+ main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
+ R.color.btn_red_background, null)));
+ }
}
}
+ else {
+ main.mainBinding.btnTagged.setVisibility(View.VISIBLE);
+ main.mainBinding.btnSaved.setVisibility(View.VISIBLE);
+ main.mainBinding.btnSaved.setText(R.string.saved);
+ main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
+ R.color.btn_orange_background, null)));
+ }
} else {
if (Utils.dataBox.getFavorite(main.userQuery) != null) {
- main.mainBinding.btnFollow.setText(R.string.unfavorite);
+ main.mainBinding.btnFollow.setText(R.string.unfavorite_short);
main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
R.color.btn_purple_background, null)));
}
else {
- main.mainBinding.btnFollow.setText(R.string.favorite);
+ main.mainBinding.btnFollow.setText(R.string.favorite_short);
main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
R.color.btn_pink_background, null)));
}
- main.mainBinding.btnFollow.setOnClickListener(profileActionListener);
main.mainBinding.btnFollow.setVisibility(View.VISIBLE);
+ if (!profileModel.isReallyPrivate()) {
+ main.mainBinding.btnRestrict.setVisibility(View.VISIBLE);
+ main.mainBinding.btnRestrict.setText(R.string.tagged);
+ main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
+ R.color.btn_blue_background, null)));
+ }
}
main.mainBinding.mainProfileImage.setEnabled(false);
@@ -864,7 +900,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0);
main.mainBinding.mainFollowing.setText(span);
- main.mainBinding.mainFullName.setText(profileModel.getName());
+ main.mainBinding.mainFullName.setText(Utils.isEmpty(profileModel.getName()) ? profileModel.getUsername() : profileModel.getName());
CharSequence biography = profileModel.getBiography();
main.mainBinding.mainBiography.setCaptionIsExpandable(true);
@@ -1163,23 +1199,36 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
private final View.OnClickListener profileActionListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
- if (!isLoggedIn && Utils.dataBox.getFavorite(main.userQuery) != null) {
+ final boolean iamme = isLoggedIn
+ ? Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)).equals(main.profileModel.getId())
+ : false;
+ if (!isLoggedIn && Utils.dataBox.getFavorite(main.userQuery) != null && v == main.mainBinding.btnFollow) {
Utils.dataBox.delFavorite(new DataBox.FavoriteModel(main.userQuery,
Long.parseLong(Utils.dataBox.getFavorite(main.userQuery).split("/")[1]),
main.locationModel != null ? main.locationModel.getName() : main.userQuery));
onRefresh();
- } else if (!isLoggedIn) {
+ } else if (!isLoggedIn && v == main.mainBinding.btnFollow) {
Utils.dataBox.addFavorite(new DataBox.FavoriteModel(main.userQuery, System.currentTimeMillis(),
main.locationModel != null ? main.locationModel.getName() : main.userQuery));
onRefresh();
} else if (v == main.mainBinding.btnFollow) {
new ProfileAction().execute("follow");
- } else if (v == main.mainBinding.btnRestrict) {
+ } else if (v == main.mainBinding.btnRestrict && isLoggedIn) {
new ProfileAction().execute("restrict");
- } else if (v == main.mainBinding.btnBlock) {
+ } else if (v == main.mainBinding.btnSaved && !iamme) {
new ProfileAction().execute("block");
} else if (v == main.mainBinding.btnFollowTag) {
new ProfileAction().execute("followtag");
+ } else if (v == main.mainBinding.btnTagged || (v == main.mainBinding.btnRestrict && !isLoggedIn)) {
+ main.startActivity(new Intent(main, SavedViewer.class)
+ .putExtra(Constants.EXTRAS_INDEX, "%"+main.profileModel.getId())
+ .putExtra(Constants.EXTRAS_USER, "@"+main.profileModel.getUsername())
+ );
+ } else if (v == main.mainBinding.btnSaved) {
+ main.startActivity(new Intent(main, SavedViewer.class)
+ .putExtra(Constants.EXTRAS_INDEX, "$"+main.profileModel.getId())
+ .putExtra(Constants.EXTRAS_USER, "@"+main.profileModel.getUsername())
+ );
}
}
};
diff --git a/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java b/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java
index 5f031251..bb458a34 100755
--- a/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java
+++ b/app/src/main/java/awais/instagrabber/activities/CommentsViewer.java
@@ -87,8 +87,6 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
new CommentsFetcher(shortCode, new FetchListener() {
@Override
public void onResult(final CommentModel[] commentModels) {
- commentsBinding.toolbar.progressCircular.setVisibility(View.GONE);
-
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
commentsBinding.rvComments.setAdapter(commentsAdapter);
@@ -105,8 +103,6 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
public void onResult(final CommentModel[] commentModels) {
commentsBinding.swipeRefreshLayout.setRefreshing(false);
- commentsBinding.toolbar.progressCircular.setVisibility(View.GONE);
-
commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
commentsBinding.rvComments.setAdapter(commentsAdapter);
diff --git a/app/src/main/java/awais/instagrabber/activities/Main.java b/app/src/main/java/awais/instagrabber/activities/Main.java
index 6a6079c6..1f16240c 100755
--- a/app/src/main/java/awais/instagrabber/activities/Main.java
+++ b/app/src/main/java/awais/instagrabber/activities/Main.java
@@ -87,7 +87,7 @@ public final class Main extends BaseLanguageActivity {
private MenuItem searchAction;
public ActivityMainBinding mainBinding;
public SearchView searchView;
- public MenuItem downloadAction, settingsAction, dmsAction;
+ public MenuItem downloadAction, settingsAction, dmsAction, notifAction;
public StoryModel[] storyModels;
public String userQuery = null;
public MainHelper mainHelper;
@@ -118,9 +118,6 @@ public final class Main extends BaseLanguageActivity {
final String uid = Utils.getUserIdFromCookie(cookie);
Utils.setupCookies(cookie);
- if (settingsHelper.getInteger(Constants.PROFILE_FETCH_MODE) == 2)
- settingsHelper.putInteger(Constants.PROFILE_FETCH_MODE, 1);
-
MainHelper.stopCurrentExecutor();
mainHelper = new MainHelper(this);
if (bundle == null) {
@@ -292,10 +289,12 @@ public final class Main extends BaseLanguageActivity {
final MenuItem quickAccessAction = menu.findItem(R.id.action_quickaccess).setVisible(true);
final MenuItem.OnMenuItemClickListener clickListener = item -> {
- if (item == downloadAction) {
+ if (item == downloadAction)
downloadSelectedItems();
- } else if (item == dmsAction)
+ else if (item == dmsAction)
startActivity(new Intent(this, DirectMessages.class));
+ else if (item == notifAction)
+ startActivity(new Intent(this, NotificationsViewer.class));
else if (item == settingsAction)
new SettingsDialog().show(fragmentManager, "settings");
else if (item == quickAccessAction)
@@ -310,11 +309,12 @@ public final class Main extends BaseLanguageActivity {
quickAccessAction.setOnMenuItemClickListener(clickListener);
menu.findItem(R.id.action_about).setVisible(true).setOnMenuItemClickListener(clickListener);
dmsAction = menu.findItem(R.id.action_dms).setOnMenuItemClickListener(clickListener);
+ notifAction = menu.findItem(R.id.action_notif).setOnMenuItemClickListener(clickListener);
settingsAction = menu.findItem(R.id.action_settings).setVisible(true).setOnMenuItemClickListener(clickListener);
downloadAction = menu.findItem(R.id.action_download).setOnMenuItemClickListener(clickListener);
if (!Utils.isEmpty(Utils.settingsHelper.getString(Constants.COOKIE))) {
- //settingsAction.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ notifAction.setVisible(true);
dmsAction.setVisible(true).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
@@ -332,6 +332,7 @@ public final class Main extends BaseLanguageActivity {
menu.findItem(R.id.action_settings).setVisible(false);
menu.findItem(R.id.action_dms).setVisible(false);
menu.findItem(R.id.action_quickaccess).setVisible(false);
+ menu.findItem(R.id.action_notif).setVisible(false);
});
searchAction.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
@@ -345,6 +346,7 @@ public final class Main extends BaseLanguageActivity {
menu.findItem(R.id.action_settings).setVisible(true);
menu.findItem(R.id.action_dms).setVisible(true);
menu.findItem(R.id.action_quickaccess).setVisible(true);
+ menu.findItem(R.id.action_notif).setVisible(true);
return true;
}
});
@@ -458,6 +460,9 @@ public final class Main extends BaseLanguageActivity {
return;
}
}
+ else {
+ finish();
+ }
}
@Override
diff --git a/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java b/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java
new file mode 100755
index 00000000..d5fda515
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java
@@ -0,0 +1,140 @@
+package awais.instagrabber.activities;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.RelativeSizeSpan;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.SearchView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import java.io.DataOutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.NotificationsAdapter;
+import awais.instagrabber.asyncs.NotificationsFetcher;
+import awais.instagrabber.databinding.ActivityNotificationBinding;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.interfaces.MentionClickListener;
+import awais.instagrabber.models.NotificationModel;
+import awais.instagrabber.models.PostModel;
+import awais.instagrabber.models.ProfileModel;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.Utils;
+
+public final class NotificationsViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
+ private NotificationsAdapter notificationsAdapter;
+ private NotificationModel notificationModel;
+ private ActivityNotificationBinding notificationsBinding;
+ private ArrayAdapter commmentDialogAdapter;
+ private String shortCode, postId, userId;
+ private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
+ private Resources resources;
+ private InputMethodManager imm;
+
+ @Override
+ protected void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ notificationsBinding = ActivityNotificationBinding.inflate(getLayoutInflater());
+ setContentView(notificationsBinding.getRoot());
+ notificationsBinding.swipeRefreshLayout.setOnRefreshListener(this);
+
+ notificationsBinding.swipeRefreshLayout.setRefreshing(true);
+ setSupportActionBar(notificationsBinding.toolbar.toolbar);
+ notificationsBinding.toolbar.toolbar.setTitle(R.string.title_notifications);
+
+ resources = getResources();
+
+ new NotificationsFetcher(new FetchListener() {
+ @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);
+ }
+
+ @Override
+ public void onRefresh() {
+ notificationsBinding.swipeRefreshLayout.setRefreshing(true);
+ new NotificationsFetcher(new FetchListener() {
+ @Override
+ public void onResult(final NotificationModel[] notificationModels) {
+ notificationsBinding.swipeRefreshLayout.setRefreshing(false);
+
+ notificationsAdapter = new NotificationsAdapter(notificationModels, clickListener, mentionClickListener);
+
+ notificationsBinding.rvComments.setAdapter(notificationsAdapter);
+ }
+ }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
+ if (which == 0)
+ searchUsername(notificationModel.getUsername());
+ else if (which == 1)
+ startActivity(new Intent(getApplicationContext(), PostViewer.class)
+ .putExtra(Constants.EXTRAS_POST, new PostModel(notificationModel.getShortcode())));
+ };
+
+ private final View.OnClickListener clickListener = v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof NotificationModel) {
+ notificationModel = (NotificationModel) tag;
+
+ final String username = notificationModel.getUsername();
+ final SpannableString title = new SpannableString(username + ":\n" + notificationModel.getText());
+ title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ String[] commentDialogList;
+
+ if (notificationModel.getShortcode() != null) commentDialogList = new String[]{
+ resources.getString(R.string.open_profile),
+ resources.getString(R.string.view_post)
+ };
+ else commentDialogList = new String[]{
+ resources.getString(R.string.open_profile)
+ };
+
+ commmentDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, commentDialogList);
+
+ new AlertDialog.Builder(this).setTitle(title)
+ .setAdapter(commmentDialogAdapter, profileDialogListener)
+ .setNeutralButton(R.string.cancel, null)
+ .show();
+ }
+ };
+
+ private final MentionClickListener mentionClickListener = (view, text, isHashtag) ->
+ new AlertDialog.Builder(this).setTitle(text)
+ .setMessage(isHashtag ? R.string.comment_view_mention_hash_search : R.string.comment_view_mention_user_search)
+ .setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok,
+ (dialog, which) -> searchUsername(text)).show();
+
+
+ private void searchUsername(final String text) {
+ if (Main.scanHack != null) {
+ Main.scanHack.onResult(text);
+ setResult(6969);
+ finish();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/activities/PostViewer.java b/app/src/main/java/awais/instagrabber/activities/PostViewer.java
index d197a118..6dc0c4ba 100755
--- a/app/src/main/java/awais/instagrabber/activities/PostViewer.java
+++ b/app/src/main/java/awais/instagrabber/activities/PostViewer.java
@@ -268,7 +268,11 @@ public final class PostViewer extends BaseLanguageActivity {
final List extends BasePostModel> itemGetterItems;
final boolean isMainSwipe;
- if (itemGetType != null && Main.itemGetter != null) {
+ if (itemGetType == ItemGetType.SAVED_ITEMS && SavedViewer.itemGetter != null) {
+ itemGetterItems = SavedViewer.itemGetter.get(itemGetType);
+ isMainSwipe = !(itemGetterItems.size() < 1 || itemGetType == ItemGetType.SAVED_ITEMS && isFromShare);
+ }
+ else if (itemGetType != null && Main.itemGetter != null) {
itemGetterItems = Main.itemGetter.get(itemGetType);
isMainSwipe = !(itemGetterItems.size() < 1 || itemGetType == ItemGetType.MAIN_ITEMS && isFromShare);
} else {
diff --git a/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java b/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java
index 9343abf9..287294f7 100755
--- a/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java
+++ b/app/src/main/java/awais/instagrabber/activities/ProfileViewer.java
@@ -7,7 +7,6 @@ import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
-import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -29,22 +28,14 @@ import awais.instagrabber.R;
import awais.instagrabber.asyncs.DownloadAsync;
import awais.instagrabber.asyncs.ProfilePictureFetcher;
import awais.instagrabber.databinding.ActivityProfileBinding;
-import awais.instagrabber.dialogs.ProfileSettingsDialog;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.HashtagModel;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.ProfileModel;
-import awais.instagrabber.models.enums.ProfilePictureFetchMode;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
-import static awais.instagrabber.utils.Constants.PROFILE_FETCH_MODE;
-
public final class ProfileViewer extends BaseLanguageActivity {
- private final ProfilePictureFetchMode[] fetchModes = {
- ProfilePictureFetchMode.INSTADP,
- ProfilePictureFetchMode.INSTAFULLSIZE
- };
private ActivityProfileBinding profileBinding;
private ProfileModel profileModel;
private HashtagModel hashtagModel;
@@ -88,15 +79,12 @@ public final class ProfileViewer extends BaseLanguageActivity {
profileBinding.imageViewer.setZoomTransitionDuration(420);
profileBinding.imageViewer.setMaximumScale(7.2f);
- final int fetchIndex = Math.min(2, Math.max(0, Utils.settingsHelper.getInteger(PROFILE_FETCH_MODE)));
- final ProfilePictureFetchMode fetchMode = fetchModes[fetchIndex];
-
fetchListener = profileUrl -> {
profilePicUrl = profileUrl;
if (!fallbackToProfile && Utils.isEmpty(profilePicUrl)) {
fallbackToProfile = true;
- new ProfilePictureFetcher(username, id, fetchListener, fetchMode, profilePicUrl, (hashtagModel != null || locationModel != null)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return;
}
@@ -113,7 +101,7 @@ public final class ProfileViewer extends BaseLanguageActivity {
fallbackToProfile = true;
if (!errorHandled) {
errorHandled = true;
- new ProfilePictureFetcher(username, id, fetchListener, fetchModes[Math.min(2, Math.max(0, fetchIndex + 1))], profilePicUrl, (hashtagModel != null || locationModel != null))
+ new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null))
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
glideRequestManager.load(profilePicUrl).into(profileBinding.imageViewer);
@@ -168,7 +156,7 @@ public final class ProfileViewer extends BaseLanguageActivity {
}).into(profileBinding.imageViewer);
};
- new ProfilePictureFetcher(username, id, fetchListener, fetchMode, profilePicUrl, (hashtagModel != null || locationModel != null))
+ new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null))
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@@ -211,8 +199,6 @@ public final class ProfileViewer extends BaseLanguageActivity {
final MenuItem.OnMenuItemClickListener menuItemClickListener = item -> {
if (item == menuItemDownload) {
downloadProfilePicture();
- } else {
- new ProfileSettingsDialog().show(fragmentManager, "settings");
}
return true;
};
@@ -223,10 +209,6 @@ public final class ProfileViewer extends BaseLanguageActivity {
menuItemDownload.setEnabled(false);
menuItemDownload.setOnMenuItemClickListener(menuItemClickListener);
- final MenuItem menuItemSettings = menu.findItem(R.id.action_settings);
- menuItemSettings.setVisible(true);
- menuItemSettings.setOnMenuItemClickListener(menuItemClickListener);
-
return true;
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/activities/SavedViewer.java b/app/src/main/java/awais/instagrabber/activities/SavedViewer.java
new file mode 100755
index 00000000..191d5f06
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/activities/SavedViewer.java
@@ -0,0 +1,234 @@
+package awais.instagrabber.activities;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import awais.instagrabber.BuildConfig;
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.PostsAdapter;
+import awais.instagrabber.asyncs.PostsFetcher;
+import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
+import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
+import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
+import awais.instagrabber.databinding.ActivitySavedBinding;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.interfaces.ItemGetter;
+import awais.instagrabber.models.BasePostModel;
+import awais.instagrabber.models.PostModel;
+import awais.instagrabber.models.enums.DownloadMethod;
+import awais.instagrabber.models.enums.ItemGetType;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.Utils;
+import awaisomereport.LogCollector;
+
+import static awais.instagrabber.utils.Constants.AUTOLOAD_POSTS;
+import static awais.instagrabber.utils.Utils.logCollector;
+
+public final class SavedViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
+ private static AsyncTask, ?, ?> currentlyExecuting;
+ public static ItemGetter itemGetter;
+ private PostsAdapter postsAdapter;
+ private boolean hasNextPage, autoloadPosts;
+ //private CommentModel commentModel;
+ private ActivitySavedBinding savedBinding;
+ private String action, username, endCursor;
+ private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
+ private RecyclerLazyLoader lazyLoader;
+ private Resources resources;
+ private ArrayList selectedItems = new ArrayList<>();
+ private final ArrayList allItems = new ArrayList<>();
+ private MenuItem downloadAction;
+
+ private final FetchListener postsFetchListener = new FetchListener() {
+ @Override
+ public void onResult(final PostModel[] result) {
+ if (result != null) {
+ final int oldSize = allItems.size();
+ allItems.addAll(Arrays.asList(result));
+
+ postsAdapter.notifyItemRangeInserted(oldSize, result.length);
+
+ savedBinding.mainPosts.post(() -> {
+ savedBinding.mainPosts.setNestedScrollingEnabled(true);
+ savedBinding.mainPosts.setVisibility(View.VISIBLE);
+ });
+
+ final PostModel model = result[result.length - 1];
+ if (model != null) {
+ endCursor = model.getEndCursor();
+
+ hasNextPage = model.hasNextPage();
+ if (autoloadPosts && hasNextPage)
+ currentlyExecuting = new PostsFetcher(action, endCursor, this)
+ .setUsername(username).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ else {
+ savedBinding.swipeRefreshLayout.setRefreshing(false);
+ }
+ model.setPageCursor(false, null);
+ }
+ }
+ else {
+ savedBinding.swipeRefreshLayout.setRefreshing(false);
+ Toast.makeText(getApplicationContext(), R.string.empty_list, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ savedBinding = ActivitySavedBinding.inflate(getLayoutInflater());
+ setContentView(savedBinding.getRoot());
+ savedBinding.swipeRefreshLayout.setOnRefreshListener(this);
+ autoloadPosts = Utils.settingsHelper.getBoolean(AUTOLOAD_POSTS);
+ savedBinding.mainPosts.setNestedScrollingEnabled(false);
+ final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(this, Utils.convertDpToPx(110));
+ savedBinding.mainPosts.setLayoutManager(layoutManager);
+ savedBinding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
+
+ final Intent intent = getIntent();
+ if (intent == null || !intent.hasExtra(Constants.EXTRAS_INDEX)
+ || Utils.isEmpty((action = intent.getStringExtra(Constants.EXTRAS_INDEX)))
+ || !intent.hasExtra(Constants.EXTRAS_USER)
+ || Utils.isEmpty((username = intent.getStringExtra(Constants.EXTRAS_USER)))) {
+ Utils.errorFinish(this);
+ return;
+ }
+
+ savedBinding.mainPosts.setAdapter(postsAdapter = new PostsAdapter(allItems, v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof PostModel) {
+ final PostModel postModel = (PostModel) tag;
+
+ if (postsAdapter.isSelecting) toggleSelection(postModel);
+ else startActivity(new Intent(this, PostViewer.class)
+ .putExtra(Constants.EXTRAS_INDEX, postModel.getPosition())
+ .putExtra(Constants.EXTRAS_POST, postModel)
+ .putExtra(Constants.EXTRAS_USER, username)
+ .putExtra(Constants.EXTRAS_TYPE, ItemGetType.SAVED_ITEMS));
+ }
+ }, v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof PostModel) {
+ postsAdapter.isSelecting = true;
+ toggleSelection((PostModel) tag);
+ }
+ return true;
+ }));
+ savedBinding.swipeRefreshLayout.setRefreshing(true);
+ setSupportActionBar(savedBinding.toolbar.toolbar);
+ savedBinding.toolbar.toolbar.setTitle((action.charAt(0) == '$' ? R.string.saved :
+ (action.charAt(0) == '%' ? R.string.tagged : R.string.liked)));
+ savedBinding.toolbar.toolbar.setSubtitle(username);
+
+ lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
+ if (!autoloadPosts && hasNextPage) {
+ savedBinding.swipeRefreshLayout.setRefreshing(true);
+ stopCurrentExecutor();
+ currentlyExecuting = new PostsFetcher(action, endCursor, postsFetchListener)
+ .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ endCursor = null;
+ }
+ });
+ savedBinding.mainPosts.addOnScrollListener(lazyLoader);
+
+ itemGetter = itemGetType -> {
+ if (itemGetType == ItemGetType.SAVED_ITEMS) return allItems;
+ return null;
+ };
+
+ new PostsFetcher(action, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.saved, menu);
+
+ downloadAction = menu.findItem(R.id.downloadAction);
+ downloadAction.setVisible(false);
+
+ downloadAction.setOnMenuItemClickListener(item -> {
+ if (selectedItems.size() > 0) {
+ Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
+ }
+ return true;
+ });
+ return true;
+ }
+
+ public void deselectSelection(final BasePostModel postModel) {
+ if (postModel instanceof PostModel) {
+ selectedItems.remove(postModel);
+ postModel.setSelected(false);
+ if (postsAdapter != null) notifyAdapter((PostModel) postModel);
+ }
+ }
+
+ @Override
+ public void onRefresh() {
+ if (lazyLoader != null) lazyLoader.resetState();
+ stopCurrentExecutor();
+ allItems.clear();
+ selectedItems.clear();
+ if (postsAdapter != null) {
+ postsAdapter.isSelecting = false;
+ postsAdapter.notifyDataSetChanged();
+ }
+ savedBinding.swipeRefreshLayout.setRefreshing(true);
+ new PostsFetcher(action, postsFetchListener).setUsername(username).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED && selectedItems.size() > 0)
+ Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
+ }
+
+ public static void stopCurrentExecutor() {
+ if (currentlyExecuting != null) {
+ try {
+ currentlyExecuting.cancel(true);
+ } catch (final Exception e) {
+ if (logCollector != null)
+ logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
+ if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
+ }
+ }
+ }
+
+ private void toggleSelection(final PostModel postModel) {
+ if (postModel != null && postsAdapter != null) {
+ if (postModel.isSelected()) selectedItems.remove(postModel);
+ else selectedItems.add(postModel);
+ postModel.setSelected(!postModel.isSelected());
+ notifyAdapter(postModel);
+ }
+ }
+
+ private void notifyAdapter(final PostModel postModel) {
+ if (selectedItems.size() < 1) postsAdapter.isSelecting = false;
+ if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged();
+ else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel);
+
+ if (downloadAction != null) {
+ downloadAction.setVisible(postsAdapter.isSelecting);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java
index c657f79c..78c73cc2 100755
--- a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java
+++ b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java
@@ -15,10 +15,12 @@ import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.widget.ArrayAdapter;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GestureDetectorCompat;
@@ -36,8 +38,11 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
@@ -47,6 +52,7 @@ import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.ActivityStoryViewerBinding;
import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.FeedStoryModel;
+import awais.instagrabber.models.PollModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType;
@@ -79,6 +85,7 @@ public final class StoryViewer extends BaseLanguageActivity {
private SimpleExoPlayer player;
private SwipeEvent swipeEvent;
private MenuItem menuDownload;
+ private PollModel poll;
private StoryModel currentStory;
private String url, username;
private int slidePos = 0, lastSlidePos = 0;
@@ -121,8 +128,6 @@ public final class StoryViewer extends BaseLanguageActivity {
@Override
public void onSwipe(final boolean isRightSwipe) {
- Log.d("austin_debug", "swipe: "+(isRightSwipe ? "backward " : "forward ") + slidePos + "/" + storiesLen + " "
- + (slidePos == storiesLen - 1 && isRightSwipe == false) + " " + intent.hasExtra(Constants.FEED));
if (storyModels != null && storiesLen > 0) {
if (((slidePos + 1 >= storiesLen && isRightSwipe == false) || (slidePos == 0 && isRightSwipe == true))
&& intent.hasExtra(Constants.FEED)) {
@@ -189,12 +194,45 @@ public final class StoryViewer extends BaseLanguageActivity {
return false;
});
+ storyViewerBinding.spotify.setOnClickListener(v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof CharSequence) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(tag.toString()));
+ startActivity(intent);
+ }
+ });
+
storyViewerBinding.viewStoryPost.setOnClickListener(v -> {
final Object tag = v.getTag();
if (tag instanceof CharSequence) startActivity(new Intent(this, PostViewer.class)
.putExtra(Constants.EXTRAS_POST, new PostModel(tag.toString())));
});
+ storyViewerBinding.interactStory.setOnClickListener(v -> {
+ final Object tag = v.getTag();
+ if (tag instanceof PollModel) {
+ poll = (PollModel) tag;
+ if (poll.getMyChoice() > -1)
+ new AlertDialog.Builder(this).setTitle(R.string.voted_story_poll)
+ .setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new String[]{
+ (poll.getMyChoice() == 0 ? "√ " : "") + poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
+ (poll.getMyChoice() == 1 ? "√ " : "") + poll.getRightChoice() + " (" + poll.getRightCount() + ")"
+ }), null)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ else new AlertDialog.Builder(this).setTitle(poll.getQuestion())
+ .setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new String[]{
+ poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
+ poll.getRightChoice() + " (" + poll.getRightCount() + ")"
+ }), (d, w) -> {
+ new VoteAction().execute(w);
+ })
+ .setPositiveButton(R.string.cancel, null)
+ .show();
+ }
+ });
+
storiesAdapter.setData(storyModels);
if (storyModels.length > 1) storyViewerBinding.storiesList.setVisibility(View.VISIBLE);
@@ -368,9 +406,18 @@ public final class StoryViewer extends BaseLanguageActivity {
storyViewerBinding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE);
storyViewerBinding.viewStoryPost.setTag(shortCode);
+ final String spotify = currentStory.getSpotify();
+ storyViewerBinding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE);
+ storyViewerBinding.spotify.setTag(spotify);
+
+ final PollModel poll = currentStory.getPoll();
+ storyViewerBinding.interactStory.setVisibility(poll != null ? View.VISIBLE : View.GONE);
+ storyViewerBinding.interactStory.setText(R.string.vote_story_poll);
+ storyViewerBinding.interactStory.setTag(poll);
+
releasePlayer();
final Intent intent = getIntent();
- if (intent.hasExtra(Constants.EXTRAS_HASHTAG)) {
+ if (intent.getBooleanExtra(Constants.EXTRAS_HASHTAG, false)) {
storyViewerBinding.toolbar.toolbar.setTitle(currentStory.getUsername() + " (" + intent.getStringExtra(Constants.EXTRAS_USERNAME) + ")");
storyViewerBinding.toolbar.toolbar.setOnClickListener(v -> {
searchUsername(currentStory.getUsername());
@@ -408,4 +455,46 @@ public final class StoryViewer extends BaseLanguageActivity {
}
return returnvalue;
}
+
+ class VoteAction extends AsyncTask {
+ int ok = -1;
+ String action;
+
+ protected Void doInBackground(Integer... rawchoice) {
+ int choice = rawchoice[0];
+ final String url = "https://www.instagram.com/media/"+currentStory.getStoryMediaId()+"/"+poll.getId()+"/story_poll_vote/";
+ try {
+ final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
+ urlConnection.setRequestMethod("POST");
+ urlConnection.setUseCaches(false);
+ urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
+ urlConnection.setRequestProperty("x-csrftoken",
+ settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
+ urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ urlConnection.setRequestProperty("Content-Length", "6");
+ urlConnection.setDoOutput(true);
+ DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
+ wr.writeBytes("vote="+choice);
+ wr.flush();
+ wr.close();
+ urlConnection.connect();
+ if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ ok = choice;
+ }
+ else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
+ urlConnection.disconnect();
+ } catch (Throwable ex) {
+ Log.e("austin_debug", "vote: " + ex);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (ok > -1) {
+ poll.setMyChoice(ok);
+ Toast.makeText(getApplicationContext(), R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java b/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
index 526984ab..c223040e 100755
--- a/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
@@ -425,8 +425,6 @@ public final class FeedAdapter extends RecyclerView.Adapter
if (btnMute != null) btnMute.setVisibility(View.VISIBLE);
final PlayerView playerView = new PlayerView(context);
- Log.d("austin_debug","1");
-
player = new SimpleExoPlayer.Builder(context).build();
playerView.setPlayer(player);
diff --git a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
new file mode 100755
index 00000000..3afa2ad0
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
@@ -0,0 +1,90 @@
+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 com.bumptech.glide.request.RequestOptions;
+
+import java.util.ArrayList;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
+import awais.instagrabber.interfaces.MentionClickListener;
+import awais.instagrabber.models.NotificationModel;
+import awais.instagrabber.utils.LocaleUtils;
+import awais.instagrabber.utils.Utils;
+
+public final class NotificationsAdapter extends RecyclerView.Adapter {
+ private final View.OnClickListener onClickListener;
+ private final MentionClickListener mentionClickListener;
+ private final NotificationModel[] notificationModels;
+ private LayoutInflater layoutInflater;
+
+ public NotificationsAdapter(final NotificationModel[] notificationModels, final View.OnClickListener onClickListener,
+ final MentionClickListener mentionClickListener) {
+ this.notificationModels = notificationModels;
+ this.onClickListener = onClickListener;
+ this.mentionClickListener = mentionClickListener;
+ }
+
+ @NonNull
+ @Override
+ public NotificationViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
+ final Context context = parent.getContext();
+ if (layoutInflater == null) layoutInflater = LayoutInflater.from(context);
+ return new NotificationViewHolder(layoutInflater.inflate(R.layout.item_notification,
+ parent, false), onClickListener, mentionClickListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull final NotificationViewHolder holder, final int position) {
+ final NotificationModel notificationModel = notificationModels[position];
+ if (notificationModel != null) {
+ holder.setNotificationModel(notificationModel);
+
+ int text = -1;
+ CharSequence subtext = null;
+ switch (notificationModel.getType()) {
+ case LIKE:
+ text = R.string.liked_notif;
+ break;
+ case COMMENT:
+ text = R.string.comment_notif;
+ subtext = notificationModel.getText();
+ break;
+ case MENTION:
+ text = R.string.mention_notif;
+ subtext = notificationModel.getText();
+ break;
+ case FOLLOW:
+ text = R.string.follow_notif;
+ break;
+ }
+
+ holder.setCommment(text);
+ holder.setSubCommment(subtext);
+ holder.setDate(notificationModel.getDateTime());
+
+ holder.setUsername(notificationModel.getUsername());
+
+ final RequestManager rm = Glide.with(layoutInflater.getContext())
+ .applyDefaultRequestOptions(new RequestOptions().skipMemoryCache(true));
+
+ rm.load(notificationModel.getProfilePic()).into(holder.getProfilePicView());
+ rm.load(notificationModel.getPreviewPic()).into(holder.getPreviewPicView());
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return notificationModels == null ? 0 : notificationModels.length;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
new file mode 100755
index 00000000..261a4aff
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
@@ -0,0 +1,76 @@
+package awais.instagrabber.adapters.viewholder;
+
+import android.text.Spannable;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.CommentsAdapter;
+import awais.instagrabber.customviews.RamboTextView;
+import awais.instagrabber.interfaces.MentionClickListener;
+import awais.instagrabber.models.NotificationModel;
+
+public final class NotificationViewHolder extends RecyclerView.ViewHolder {
+ private final MentionClickListener mentionClickListener;
+ private final ImageView ivProfilePic, ivPreviewPic;
+ private final TextView tvUsername, tvDate, tvComment, tvSubComment;
+ private final View container, rightContainer;
+
+ public NotificationViewHolder(@NonNull final View itemView, final View.OnClickListener onClickListener, final MentionClickListener mentionClickListener) {
+ super(itemView);
+
+ container = itemView.findViewById(R.id.container);
+ rightContainer = itemView.findViewById(R.id.rightContainer);
+ if (onClickListener != null) container.setOnClickListener(onClickListener);
+
+ this.mentionClickListener = mentionClickListener;
+
+ ivProfilePic = itemView.findViewById(R.id.ivProfilePic);
+ ivPreviewPic = itemView.findViewById(R.id.ivPreviewPic);
+ tvUsername = itemView.findViewById(R.id.tvUsername);
+ tvDate = itemView.findViewById(R.id.tvDate);
+ tvComment = itemView.findViewById(R.id.tvComment);
+ tvSubComment = itemView.findViewById(R.id.tvSubComment);
+
+ tvUsername.setSelected(true);
+ tvDate.setSelected(true);
+ }
+
+ public final ImageView getProfilePicView() {
+ return ivProfilePic;
+ }
+
+ public final ImageView getPreviewPicView() {
+ return ivPreviewPic;
+ }
+
+ public final void setNotificationModel(final NotificationModel notificationModel) {
+ if (container != null) container.setTag(notificationModel);
+ if (rightContainer != null) rightContainer.setTag(notificationModel);
+ }
+
+ public final void setUsername(final String username) {
+ if (tvUsername != null) tvUsername.setText(username);
+ }
+
+ public final void setDate(final String date) {
+ if (tvDate != null) tvDate.setText(date);
+ }
+
+ public final void setCommment(final int commment) {
+ if (tvComment != null) {
+ tvComment.setText(commment);
+ }
+ }
+
+ public final void setSubCommment(final CharSequence commment) {
+ if (tvSubComment != null) {
+ tvSubComment.setText(commment, commment instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
+ ((RamboTextView) tvSubComment).setMentionClickListener(mentionClickListener);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
index 4569a4f4..e0459b07 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/DiscoverFetcher.java
@@ -24,6 +24,7 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
+import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
@@ -45,17 +46,10 @@ public final class DiscoverFetcher extends AsyncTask discoverItemModels = fetchItems(downloadDir, customDir, null, maxId);
+ final ArrayList discoverItemModels = fetchItems(null, maxId);
if (discoverItemModels != null) {
result = discoverItemModels.toArray(new DiscoverItemModel[0]);
if (result.length > 0) {
@@ -67,8 +61,7 @@ public final class DiscoverFetcher extends AsyncTask fetchItems(final File downloadDir, final File customDir,
- ArrayList discoverItemModels, final String maxId) {
+ private ArrayList fetchItems(ArrayList discoverItemModels, final String maxId) {
try {
final String url = "https://www.instagram.com/explore/grid/?is_prefetch=false&omit_cover_media=true&module=explore_popular" +
"&use_sectional_payload=false&cluster_id=explore_all%3A0&include_fixed_destinations=true" + maxId;
@@ -99,8 +92,7 @@ public final class DiscoverFetcher extends AsyncTask 50) this.isFirst = false;
- discoverItemModels = fetchItems(downloadDir, customDir, discoverItemModels,
- "&max_id=" + (lastId++));
+ discoverItemModels = fetchItems(discoverItemModels, "&max_id=" + (lastId++));
}
} else {
urlConnection.disconnect();
@@ -149,8 +138,7 @@ public final class DiscoverFetcher extends AsyncTask
private final String idSlug;
public LocationFetcher(String idSlug, FetchListener fetchListener) {
- Log.d("austin_debug", idSlug);
// idSlug = id + "/" + slug
this.idSlug = idSlug;
this.fetchListener = fetchListener;
diff --git a/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
new file mode 100755
index 00000000..5e770910
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
@@ -0,0 +1,85 @@
+package awais.instagrabber.asyncs;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import awais.instagrabber.BuildConfig;
+import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.models.NotificationModel;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.Utils;
+import awaisomereport.LogCollector;
+
+import static awais.instagrabber.utils.Utils.logCollector;
+
+public final class NotificationsFetcher extends AsyncTask {
+ private final FetchListener fetchListener;
+
+ public NotificationsFetcher(final FetchListener fetchListener) {
+ this.fetchListener = fetchListener;
+ }
+
+ @Override
+ protected NotificationModel[] doInBackground(final Void... voids) {
+ NotificationModel[] result = null;
+ final String url = "https://www.instagram.com/accounts/activity/?__a=1";
+
+ try {
+ final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+ conn.setInstanceFollowRedirects(false);
+ conn.setUseCaches(false);
+ conn.connect();
+
+ if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ JSONObject data = new JSONObject(Utils.readFromConnection(conn))
+ .getJSONObject("graphql").getJSONObject("user").getJSONObject("activity_feed").getJSONObject("edge_web_activity_feed");
+
+ JSONArray media;
+ if ((media = data.optJSONArray("edges")) != null && media.length() > 0 &&
+ (data = media.optJSONObject(0).optJSONObject("node")) != null) {
+
+ final int mediaLen = media.length();
+
+ final NotificationModel[] models = new NotificationModel[mediaLen];
+ for (int i = 0; i < mediaLen; ++i) {
+ data = media.optJSONObject(i).optJSONObject("node");
+ if (Utils.getNotifType(data.getString("__typename")) == null) continue;
+ models[i] = new NotificationModel(data.getString(Constants.EXTRAS_ID),
+ data.optString("text"), // comments or mentions
+ data.getLong("timestamp"),
+ data.getJSONObject("user").getString("username"),
+ data.getJSONObject("user").getString("profile_pic_url"),
+ !data.isNull("media") ? data.getJSONObject("media").getString("shortcode") : null,
+ !data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null,
+ Utils.getNotifType(data.getString("__typename")));
+ }
+ result = models;
+ }
+ }
+
+ conn.disconnect();
+ } catch (final Exception e) {
+ if (logCollector != null)
+ logCollector.appendException(e, LogCollector.LogFile.ASYNC_NOTIFICATION_FETCHER, "doInBackground");
+ if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (fetchListener != null) fetchListener.doBefore();
+ }
+
+ @Override
+ protected void onPostExecute(final NotificationModel[] result) {
+ if (fetchListener != null) fetchListener.onResult(result);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
index c201f7a9..7ecc2d5e 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/PostFetcher.java
@@ -19,6 +19,7 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
+import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
@@ -41,19 +42,22 @@ public final class PostFetcher extends AsyncTask
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- // to check if file exists
- final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download");
- File customDir = null;
- if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
- final String customPath = Utils.settingsHelper.getString(FOLDER_PATH);
- if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
- }
final JSONObject media = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("graphql")
.getJSONObject("shortcode_media");
final String username = media.has("owner") ? media.getJSONObject("owner").getString(Constants.EXTRAS_USERNAME) : null;
+ // to check if file exists
+ final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
+ (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
+ File customDir = null;
+ if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
+ final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
+ (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
+ if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
+ }
+
final long timestamp = media.getLong("taken_at_timestamp");
final boolean isVideo = media.has("is_video") && media.optBoolean("is_video");
@@ -95,7 +99,7 @@ public final class PostFetcher extends AsyncTask
postModel.setCommentsCount(commentsCount);
postModel.setCommentsEndCursor(endCursor);
- Utils.checkExistence(downloadDir, customDir, username, false, -1, postModel);
+ Utils.checkExistence(downloadDir, customDir, false, postModel);
result = new ViewerPostModel[]{postModel};
@@ -119,7 +123,7 @@ public final class PostFetcher extends AsyncTask
media.optJSONObject("location"));
postModels[i].setSliderDisplayUrl(node.getString("display_url"));
- Utils.checkExistence(downloadDir, customDir, username, true, i, postModels[i]);
+ Utils.checkExistence(downloadDir, customDir, true, postModels[i]);
}
postModels[0].setCommentsCount(commentsCount);
diff --git a/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
index 7d8696a5..1b750d26 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
@@ -19,6 +19,7 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
+import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
@@ -49,6 +50,9 @@ public final class PostsFetcher extends AsyncTask {
@Override
protected PostModel[] doInBackground(final Void... voids) {
final boolean isHashTag = id.charAt(0) == '#';
+ final boolean isSaved = id.charAt(0) == '$';
+ final boolean isTagged = id.charAt(0) == '%';
+ //final boolean isLiked = id.charAt(0) == '^';
final boolean isLocation = id.contains("/");
final String url;
@@ -58,6 +62,12 @@ public final class PostsFetcher extends AsyncTask {
else if (isLocation)
url = "https://www.instagram.com/graphql/query/?query_hash=36bd0f2bf5911908de389b8ceaa3be6d&variables=" +
"{\"id\":\""+ id.split("/")[0] +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
+ else if (isSaved)
+ url = "https://www.instagram.com/graphql/query/?query_hash=8c86fed24fa03a8a2eea2a70a80c7b6b&variables=" +
+ "{\"id\":\""+ id.substring(1) +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
+ else if (isTagged)
+ url = "https://www.instagram.com/graphql/query/?query_hash=ff260833edf142911047af6024eb634a&variables=" +
+ "{\"id\":\""+ id.substring(1) +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
else
url = "https://www.instagram.com/graphql/query/?query_id=17880160963012870&id=" + id + "&first=50&after=" + endCursor;
@@ -69,10 +79,12 @@ public final class PostsFetcher extends AsyncTask {
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
// to check if file exists
- final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download");
+ final File downloadDir = new File(Environment.getExternalStorageDirectory(), "Download" +
+ (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
File customDir = null;
if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
- final String customPath = Utils.settingsHelper.getString(FOLDER_PATH);
+ final String customPath = Utils.settingsHelper.getString(FOLDER_PATH +
+ (Utils.settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER) ? ("/"+username) : ""));
if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
}
@@ -80,7 +92,9 @@ public final class PostsFetcher extends AsyncTask {
.getJSONObject(isHashTag ? Constants.EXTRAS_HASHTAG :
(isLocation ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_USER))
.getJSONObject(isHashTag ? "edge_hashtag_to_media" :
- (isLocation ? "edge_location_to_media" : "edge_owner_to_timeline_media"));
+ (isLocation ? "edge_location_to_media" :
+ (isSaved ? "edge_saved_media" :
+ (isTagged ? "edge_user_to_photos_of_you" : "edge_owner_to_timeline_media"))));
final String endCursor;
final boolean hasNextPage;
@@ -115,7 +129,7 @@ public final class PostsFetcher extends AsyncTask {
mediaNode.getLong("taken_at_timestamp"), mediaNode.optBoolean("viewer_has_liked"),
mediaNode.optBoolean("viewer_has_saved"), mediaNode.getJSONObject("edge_liked_by").getLong("count"));
- Utils.checkExistence(downloadDir, customDir, username, isSlider, -1, models[i]);
+ Utils.checkExistence(downloadDir, customDir, isSlider, models[i]);
}
if (models[models.length - 1] != null)
diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
index 4607b761..802fff03 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java
@@ -5,31 +5,26 @@ import android.util.Log;
import android.util.Pair;
import org.json.JSONObject;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.jsoup.select.Elements;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
+import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
-import awais.instagrabber.models.enums.ProfilePictureFetchMode;
+
import static awais.instagrabber.utils.Utils.logCollector;
public final class ProfilePictureFetcher extends AsyncTask {
private final FetchListener fetchListener;
private final String userName, userId, picUrl;
private final boolean isHashtag;
- private ProfilePictureFetchMode fetchMode;
public ProfilePictureFetcher(final String userName, final String userId, final FetchListener fetchListener,
- final ProfilePictureFetchMode fetchMode, final String picUrl, final boolean isHashtag) {
+ final String picUrl, final boolean isHashtag) {
this.fetchListener = fetchListener;
- this.fetchMode = fetchMode;
this.userName = userName;
this.userId = userId;
this.picUrl = picUrl;
@@ -40,69 +35,28 @@ public final class ProfilePictureFetcher extends AsyncTask {
protected String doInBackground(final Void... voids) {
String out = picUrl;
if (!isHashtag) try {
- if (fetchMode == ProfilePictureFetchMode.INSTA_STALKER) fetchMode = ProfilePictureFetchMode.INSTADP;
- final String url;
-
- if (fetchMode == ProfilePictureFetchMode.INSTADP)
- url = "https://instadp.com/fullsize/" + userName;
- else // select from s1, s2, s3 but s1 works fine
- url = "https://instafullsize.com/ifsapi/ig/photo/s1/" + userName + "?igid=" + userId;
-
- // prolly http://167.99.85.4/instagram/userid?profile-url=the.badak
+ final String url = "https://i.instagram.com/api/v1/users/"+userId+"/info/";
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false);
-
- if (fetchMode == ProfilePictureFetchMode.INSTAFULLSIZE) {
- conn.setRequestMethod("GET");
- conn.setRequestProperty("Authorization", "fjgt842ff582a");
- }
+ conn.setRequestMethod("GET");
+ conn.setRequestProperty("User-Agent", Constants.USER_AGENT);
final String result = conn.getResponseCode() == HttpURLConnection.HTTP_OK ? Utils.readFromConnection(conn) : null;
conn.disconnect();
if (!Utils.isEmpty(result)) {
- final Document doc = Jsoup.parse(result);
- boolean fallback = false;
-
- if (fetchMode == ProfilePictureFetchMode.INSTADP) {
- final int imgIndex = result.indexOf("preloadImg('"), lastIndex;
-
- Element element = doc.selectFirst(".instadp");
- if (element != null && (element = element.selectFirst(".picture")) != null)
- out = element.attr("src");
- else if ((element = doc.selectFirst(".download-btn")) != null)
- out = element.attr("href");
- else if (imgIndex != -1 && (lastIndex = result.indexOf("')", imgIndex)) != -1)
- out = result.substring(imgIndex + 12, lastIndex);
- else fallback = true;
-
- } else if (fetchMode == ProfilePictureFetchMode.INSTAFULLSIZE) {
- try {
- final JSONObject object = new JSONObject(result);
- out = object.getString("result");
- } catch (final Exception e) {
- if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
- fallback = true;
- }
-
- }
-
- if (fallback) {
- final Elements imgs = doc.getElementsByTag("img");
- for (final Element img : imgs) {
- final String imgStr = img.toString();
- if (imgStr.contains("cdninstagram.com")) return img.attr("src");
- }
- }
+ JSONObject data = new JSONObject(result).getJSONObject("user");
+ if (data.has("hd_profile_pic_url_info"))
+ out = data.getJSONObject("hd_profile_pic_url_info").optString("url");
}
} catch (final Exception e) {
if (logCollector != null)
- logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_PICTURE_FETCHER, "doInBackground",
- new Pair<>("fetchMode", fetchMode));
+ logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_PICTURE_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
+ if (out == null) out = picUrl;
return out;
}
diff --git a/app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java
index f885d936..2929c67a 100755
--- a/app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/StoryStatusFetcher.java
@@ -12,6 +12,7 @@ import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.enums.MediaItemType;
+import awais.instagrabber.models.PollModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
@@ -34,8 +35,8 @@ public final class StoryStatusFetcher extends AsyncTask parent, final View view, final int position, final long id) {
- settingsHelper.putInteger(PROFILE_FETCH_MODE, position);
- }
-
- @Override
- public void onNothingSelected(final AdapterView> parent) { }
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/NotificationModel.java b/app/src/main/java/awais/instagrabber/models/NotificationModel.java
new file mode 100755
index 00000000..f50c1331
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/models/NotificationModel.java
@@ -0,0 +1,58 @@
+package awais.instagrabber.models;
+
+import androidx.annotation.NonNull;
+
+import java.util.Date;
+
+import awais.instagrabber.utils.Utils;
+import awais.instagrabber.models.enums.NotificationType;
+
+public final class NotificationModel {
+ private final String id, username, profilePicUrl, shortcode, previewUrl;
+ private final NotificationType type;
+ private final CharSequence text;
+ private final long timestamp;
+
+ public NotificationModel(final String id, final String text, final long timestamp, final String username,
+ final String profilePicUrl, final String shortcode, final String previewUrl, final NotificationType type) {
+ this.id = id;
+ this.text = Utils.hasMentions(text) ? Utils.getMentionText(text) : text;
+ this.timestamp = timestamp;
+ this.username = username;
+ this.profilePicUrl = profilePicUrl;
+ this.shortcode = shortcode;
+ this.previewUrl = previewUrl;
+ this.type = type;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public CharSequence getText() {
+ return text;
+ }
+
+ @NonNull
+ public String getDateTime() {
+ return Utils.datetimeParser.format(new Date(timestamp * 1000L));
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getProfilePic() {
+ return profilePicUrl;
+ }
+
+ public String getShortcode() {
+ return shortcode;
+ }
+
+ public String getPreviewPic() {
+ return previewUrl;
+ }
+
+ public NotificationType getType() { return type; }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/PollModel.java b/app/src/main/java/awais/instagrabber/models/PollModel.java
new file mode 100755
index 00000000..3cfe063a
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/models/PollModel.java
@@ -0,0 +1,52 @@
+package awais.instagrabber.models;
+
+import java.io.Serializable;
+
+public final class PollModel implements Serializable {
+ private int leftcount, rightcount, mychoice;
+ private final String id, question, leftchoice, rightchoice;
+
+ public PollModel(final String id, final String question, final String leftchoice, final int leftcount,
+ final String rightchoice, final int rightcount, final int mychoice) {
+ this.id = id; // only the poll id
+ this.question = question;
+ this.leftchoice = leftchoice;
+ this.leftcount = leftcount;
+ this.rightchoice = rightchoice;
+ this.rightcount = rightcount;
+ this.mychoice = mychoice;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getQuestion() {
+ return question;
+ }
+
+ public String getLeftChoice() {
+ return leftchoice;
+ }
+
+ public int getLeftCount() {
+ return leftcount;
+ }
+
+ public String getRightChoice() {
+ return rightchoice;
+ }
+
+ public int getRightCount() {
+ return rightcount;
+ }
+
+ public int getMyChoice() { return mychoice; }
+
+ public int setMyChoice(final int choice) {
+ this.mychoice = choice;
+ if (choice == 0) this.leftcount += 1;
+ else if (choice == 1) this.rightcount += 1;
+ return choice;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/StoryModel.java b/app/src/main/java/awais/instagrabber/models/StoryModel.java
index 5034753a..8e367912 100755
--- a/app/src/main/java/awais/instagrabber/models/StoryModel.java
+++ b/app/src/main/java/awais/instagrabber/models/StoryModel.java
@@ -8,7 +8,8 @@ public final class StoryModel implements Serializable {
private final String storyMediaId, storyUrl, username;
private final MediaItemType itemType;
private final long timestamp;
- private String videoUrl, tappableShortCode;
+ private String videoUrl, tappableShortCode, spotify;
+ private PollModel poll;
private int position;
private boolean isCurrentSlide = false;
@@ -44,6 +45,10 @@ public final class StoryModel implements Serializable {
return tappableShortCode;
}
+ public PollModel getPoll() {
+ return poll;
+ }
+
public int getPosition() {
return position;
}
@@ -52,10 +57,22 @@ public final class StoryModel implements Serializable {
this.videoUrl = videoUrl;
}
+ public String getSpotify() {
+ return spotify;
+ }
+
+ public void setSpotify(final String spotify) {
+ this.spotify = spotify;
+ }
+
public void setTappableShortCode(final String tappableShortCode) {
this.tappableShortCode = tappableShortCode;
}
+ public void setPoll(final PollModel poll) {
+ this.poll = poll;
+ }
+
public void setPosition(final int position) {
this.position = position;
}
diff --git a/app/src/main/java/awais/instagrabber/models/enums/DownloadMethod.java b/app/src/main/java/awais/instagrabber/models/enums/DownloadMethod.java
index 9c3c9595..fc22241c 100755
--- a/app/src/main/java/awais/instagrabber/models/enums/DownloadMethod.java
+++ b/app/src/main/java/awais/instagrabber/models/enums/DownloadMethod.java
@@ -5,5 +5,6 @@ public enum DownloadMethod {
DOWNLOAD_DISCOVER,
DOWNLOAD_FEED,
DOWNLOAD_POST_VIEWER,
- DOWNLOAD_DIRECT;
+ DOWNLOAD_DIRECT,
+ DOWNLOAD_SAVED;
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/enums/ItemGetType.java b/app/src/main/java/awais/instagrabber/models/enums/ItemGetType.java
index b6e7055b..3bd66713 100755
--- a/app/src/main/java/awais/instagrabber/models/enums/ItemGetType.java
+++ b/app/src/main/java/awais/instagrabber/models/enums/ItemGetType.java
@@ -6,4 +6,5 @@ public enum ItemGetType implements Serializable {
MAIN_ITEMS,
DISCOVER_ITEMS,
FEED_ITEMS,
+ SAVED_ITEMS
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java b/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java
new file mode 100755
index 00000000..34aff6b5
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/models/enums/NotificationType.java
@@ -0,0 +1,10 @@
+package awais.instagrabber.models.enums;
+
+import java.io.Serializable;
+
+public enum NotificationType implements Serializable {
+ LIKE,
+ FOLLOW,
+ COMMENT,
+ MENTION
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/models/enums/ProfilePictureFetchMode.java b/app/src/main/java/awais/instagrabber/models/enums/ProfilePictureFetchMode.java
deleted file mode 100755
index 2285148f..00000000
--- a/app/src/main/java/awais/instagrabber/models/enums/ProfilePictureFetchMode.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package awais.instagrabber.models.enums;
-
-public enum ProfilePictureFetchMode {
- INSTADP,
- INSTAFULLSIZE,
- INSTA_STALKER,
-}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java
index 3eb85aca..e4e86d8b 100755
--- a/app/src/main/java/awais/instagrabber/utils/Constants.java
+++ b/app/src/main/java/awais/instagrabber/utils/Constants.java
@@ -10,7 +10,6 @@ public final class Constants {
public static final String APP_THEME = "app_theme";
public static final String APP_LANGUAGE = "app_language";
public static final String PREV_INSTALL_VERSION = "prevVersion";
- public static final String PROFILE_FETCH_MODE = "profile_fetch_mode";
// boolean prefs
public static final String DOWNLOAD_USER_FOLDER = "download_user_folder";
public static final String BOTTOM_TOOLBAR = "bottom_toolbar";
diff --git a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java
index 844b1dc7..28ad6d9f 100755
--- a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java
+++ b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java
@@ -219,7 +219,6 @@ public final class ExportImportUtils {
final JSONObject json = new JSONObject();
json.put(Constants.APP_THEME, settingsHelper.getInteger(Constants.APP_THEME));
json.put(Constants.APP_LANGUAGE, settingsHelper.getInteger(Constants.APP_LANGUAGE));
- json.put(Constants.PROFILE_FETCH_MODE, settingsHelper.getInteger(Constants.PROFILE_FETCH_MODE));
String str = settingsHelper.getString(Constants.FOLDER_PATH);
if (!Utils.isEmpty(str)) json.put(Constants.FOLDER_PATH, str);
diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
index d546bb6d..c638cc51 100755
--- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
+++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
@@ -23,7 +23,6 @@ import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Constants.MUTED_VIDEOS;
import static awais.instagrabber.utils.Constants.PREV_INSTALL_VERSION;
-import static awais.instagrabber.utils.Constants.PROFILE_FETCH_MODE;
import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
public final class SettingsHelper {
@@ -109,6 +108,6 @@ public final class SettingsHelper {
AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED})
public @interface BooleanSettings {}
- @StringDef({APP_THEME, APP_LANGUAGE, PROFILE_FETCH_MODE, PREV_INSTALL_VERSION})
+ @StringDef({APP_THEME, APP_LANGUAGE, PREV_INSTALL_VERSION})
public @interface IntegerSettings {}
}
\ 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 9aef2a9e..ac94d2cd 100755
--- a/app/src/main/java/awais/instagrabber/utils/Utils.java
+++ b/app/src/main/java/awais/instagrabber/utils/Utils.java
@@ -62,6 +62,7 @@ import java.util.regex.Pattern;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.activities.Main;
+import awais.instagrabber.activities.SavedViewer;
import awais.instagrabber.asyncs.DownloadAsync;
import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.customviews.CommentMentionClickSpan;
@@ -70,6 +71,7 @@ import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.models.IntentModel;
+import awais.instagrabber.models.PollModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
@@ -80,6 +82,7 @@ import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.InboxReadState;
import awais.instagrabber.models.enums.IntentModelType;
import awais.instagrabber.models.enums.MediaItemType;
+import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.models.enums.RavenExpiringMediaType;
import awais.instagrabber.models.enums.RavenMediaViewType;
import awaisomereport.LogCollector;
@@ -126,6 +129,7 @@ public final class Utils {
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());
@@ -134,6 +138,7 @@ public final class Utils {
httpCookie.setVersion(0);
cookieStore.add(uri1, httpCookie);
cookieStore.add(uri2, httpCookie);
+ cookieStore.add(uri3, httpCookie);
}
} catch (final URISyntaxException e) {
if (logCollector != null)
@@ -730,6 +735,14 @@ public final class Utils {
return RavenExpiringMediaType.RAVEN_UNKNOWN;
}
+ public static NotificationType getNotifType(final String itemType) {
+ if ("GraphLikeAggregatedStory".equals(itemType)) return NotificationType.LIKE;
+ if ("GraphFollowAggregatedStory".equals(itemType)) return NotificationType.FOLLOW;
+ if ("GraphCommentMediaStory".equals(itemType)) return NotificationType.COMMENT;
+ if ("GraphMentionStory".equals(itemType)) return NotificationType.MENTION;
+ return null;
+ }
+
public static int convertDpToPx(final float dp) {
if (displayMetrics == null)
displayMetrics = Resources.getSystem().getDisplayMetrics();
@@ -825,12 +838,14 @@ public final class Utils {
return sb.toString();
}
- public static void batchDownload(@NonNull final Context context, @Nullable final String username, final DownloadMethod method,
+ public static void batchDownload(@NonNull final Context context, @Nullable String username, final DownloadMethod method,
final List extends BasePostModel> itemsToDownload) {
if (settingsHelper == null) settingsHelper = new SettingsHelper(context);
if (itemsToDownload == null || itemsToDownload.size() < 1) return;
+ if (username.charAt(0) == '@') username = username.substring(1);
+
if (ContextCompat.checkSelfPermission(context, Utils.PERMS[0]) == PackageManager.PERMISSION_GRANTED)
batchDownloadImpl(context, username, method, itemsToDownload);
else if (context instanceof Activity)
@@ -851,6 +866,7 @@ public final class Utils {
if (dir.exists() || dir.mkdirs()) {
final Main main = method != DownloadMethod.DOWNLOAD_FEED && context instanceof Main ? (Main) context : null;
+ final SavedViewer saved = method == DownloadMethod.DOWNLOAD_SAVED && context instanceof SavedViewer ? (SavedViewer) context : null;
final int itemsToDownloadSize = itemsToDownload.size();
@@ -858,12 +874,34 @@ public final class Utils {
for (int i = itemsToDownloadSize - 1; i >= 0; i--) {
final BasePostModel selectedItem = itemsToDownload.get(i);
- if (main == null) {
+ if (main == null && saved == null) {
new DownloadAsync(context,
selectedItem.getDisplayUrl(),
getDownloadSaveFile(finalDir, selectedItem, ""),
null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else if (saved != null) {
+ new PostFetcher(selectedItem.getShortCode(), result -> {
+ if (result != null) {
+ final int resultsSize = result.length;
+ final boolean multiResult = resultsSize > 1;
+
+ for (int j = 0; j < resultsSize; j++) {
+ final BasePostModel model = result[j];
+ final File saveFile = getDownloadSaveFile(finalDir, model, multiResult ? "_slide_" + (j + 1) : "");
+
+ new DownloadAsync(context,
+ model.getDisplayUrl(),
+ saveFile,
+ file -> {
+ model.setDownloaded(true);
+ saved.deselectSelection(selectedItem);
+ }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ } else {
+ saved.deselectSelection(selectedItem);
+ }
+ }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
new PostFetcher(selectedItem.getShortCode(), result -> {
if (result != null) {
@@ -971,30 +1009,21 @@ public final class Utils {
return extension;
}
- public static void checkExistence(final File downloadDir, final File customDir, final String username, final boolean isSlider,
- final int sliderIndex, @NonNull final BasePostModel model) {
+ public static void checkExistence(final File downloadDir, final File customDir, final boolean isSlider,
+ @NonNull final BasePostModel model) {
boolean exists = false;
try {
final String displayUrl = model.getDisplayUrl();
final int index = displayUrl.indexOf('?');
- final String fileName = model.getPostId() + '_' + model.getPosition();
+ final String fileName = model.getPostId() + '_';
final String extension = displayUrl.substring(index - 4, index);
- final String fileWithoutPrefix = fileName + extension;
+ final String fileWithoutPrefix = fileName + '0' + extension;
exists = new File(downloadDir, fileWithoutPrefix).exists();
if (!exists) {
- if (customDir != null) exists = new File(customDir, fileWithoutPrefix).exists();
- if (!exists && !Utils.isEmpty(username)) {
- exists = new File(new File(downloadDir, username), fileWithoutPrefix).exists();
- }
- if (!exists && customDir != null)
- exists = new File(new File(customDir, username), fileWithoutPrefix).exists();
- }
-
- if (!exists && isSlider && sliderIndex != -1) {
- final String fileWithPrefix = fileName + "_slide_[\\d]+" + extension;
+ final String fileWithPrefix = fileName + "[\\d]+(|_slide_[\\d]+)(\\.mp4|\\" + extension + ")";
final FilenameFilter filenameFilter = (dir, name) -> Pattern.matches(fileWithPrefix, name);
File[] files = downloadDir.listFiles(filenameFilter);
@@ -1007,7 +1036,6 @@ public final class Utils {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.UTILS, "checkExistence",
new Pair<>("isSlider", isSlider),
- new Pair<>("sliderIndex", sliderIndex),
new Pair<>("model", model));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
@@ -1163,13 +1191,25 @@ public final class Utils {
if (isVideo && data.has("video_resources"))
storyModels[j].setVideoUrl(Utils.getHighQualityPost(data.getJSONArray("video_resources"), true));
+ if (!data.isNull("story_app_attribution"))
+ storyModels[j].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]);
+
if (hasTappableObjecs) {
for (int k = 0; k < tappableLength; ++k) {
JSONObject jsonObject = tappableObjects.getJSONObject(k);
if (jsonObject.getString("__typename").equals("GraphTappableFeedMedia") && jsonObject.has("media")) {
- jsonObject = jsonObject.getJSONObject("media");
- storyModels[j].setTappableShortCode(jsonObject.getString(Constants.EXTRAS_SHORTCODE));
- break;
+ storyModels[j].setTappableShortCode(jsonObject.getJSONObject("media").getString(Constants.EXTRAS_SHORTCODE));
+ }
+ else if (jsonObject.optString("__typename").equals("GraphTappableStoryPoll") && !jsonObject.isNull("id")) {
+ storyModels[j].setPoll(new PollModel(
+ jsonObject.getString("id"),
+ jsonObject.getString("question"),
+ jsonObject.getJSONArray("tallies").getJSONObject(0).getString("text"),
+ jsonObject.getJSONArray("tallies").getJSONObject(0).getInt("count"),
+ jsonObject.getJSONArray("tallies").getJSONObject(1).getString("text"),
+ jsonObject.getJSONArray("tallies").getJSONObject(1).getInt("count"),
+ jsonObject.optInt("viewer_vote", -1)
+ ));
}
}
}
diff --git a/app/src/main/java/awaisomereport/LogCollector.java b/app/src/main/java/awaisomereport/LogCollector.java
index fb1ce720..6ba7c7f2 100755
--- a/app/src/main/java/awaisomereport/LogCollector.java
+++ b/app/src/main/java/awaisomereport/LogCollector.java
@@ -108,8 +108,10 @@ public final class LogCollector {
ASYNC_FEED_FETCHER("async-feed-fetcher.txt"),
ASYNC_HASHTAG_FETCHER("async-hashtag-fetcher.txt"),
ASYNC_LOCATION_FETCHER("async-location-fetcher.txt"),
+ ASYNC_NOTIFICATION_FETCHER("async-notification-fetcher.txt"),
ASYNC_PROFILE_FETCHER("async-profile-fetcher.txt"),
ASYNC_PROFILE_PICTURE_FETCHER("async-pfp-fetcher.txt"),
+ ASYNC_SAVED_FETCHER("async-saved-fetcher.txt"),
ASYNC_STORY_STATUS_FETCHER("async-story-status-fetcher.txt"),
ASYNC_DISCOVER_FETCHER("async-discover-fetcher.txt"),
ASYNC_COMMENTS_FETCHER("async-comments-fetcher.txt"),
diff --git a/app/src/main/res/drawable-anydpi/ic_bookmark.xml b/app/src/main/res/drawable-anydpi/ic_bookmark.xml
new file mode 100644
index 00000000..69bc5089
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_bookmark.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_like.xml b/app/src/main/res/drawable-anydpi/ic_like.xml
new file mode 100644
index 00000000..f68e110e
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_like.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_notif.xml b/app/src/main/res/drawable-anydpi/ic_notif.xml
new file mode 100644
index 00000000..4a191054
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_notif.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-hdpi/ic_bookmark.png b/app/src/main/res/drawable-hdpi/ic_bookmark.png
new file mode 100644
index 00000000..8dbfafa9
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_bookmark.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_like.png b/app/src/main/res/drawable-hdpi/ic_like.png
new file mode 100644
index 00000000..3984ecbb
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_like.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_notif.png b/app/src/main/res/drawable-hdpi/ic_notif.png
new file mode 100644
index 00000000..6e71dbfa
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_notif.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_bookmark.png b/app/src/main/res/drawable-mdpi/ic_bookmark.png
new file mode 100644
index 00000000..d37d3cd9
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_bookmark.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_like.png b/app/src/main/res/drawable-mdpi/ic_like.png
new file mode 100644
index 00000000..ebc88607
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_like.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_notif.png b/app/src/main/res/drawable-mdpi/ic_notif.png
new file mode 100644
index 00000000..55db697c
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_notif.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_bookmark.png b/app/src/main/res/drawable-xhdpi/ic_bookmark.png
new file mode 100644
index 00000000..6dae7417
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_bookmark.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_like.png b/app/src/main/res/drawable-xhdpi/ic_like.png
new file mode 100644
index 00000000..eadf8e7b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_like.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_notif.png b/app/src/main/res/drawable-xhdpi/ic_notif.png
new file mode 100644
index 00000000..146975d9
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_notif.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_bookmark.png b/app/src/main/res/drawable-xxhdpi/ic_bookmark.png
new file mode 100644
index 00000000..620babe1
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_bookmark.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_like.png b/app/src/main/res/drawable-xxhdpi/ic_like.png
new file mode 100644
index 00000000..ac66ff42
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_like.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_notif.png b/app/src/main/res/drawable-xxhdpi/ic_notif.png
new file mode 100644
index 00000000..7e1866fe
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_notif.png differ
diff --git a/app/src/main/res/drawable/ic_location.png b/app/src/main/res/drawable/ic_location.png
index 39849b0d..5d4d03e1 100644
Binary files a/app/src/main/res/drawable/ic_location.png and b/app/src/main/res/drawable/ic_location.png differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 94abacf5..aeabab5b 100755
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -112,26 +112,25 @@
android:id="@+id/profileActions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_below="@id/mainUrl"
- android:weightSum="3">
+ android:layout_below="@id/mainUrl">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_saved.xml b/app/src/main/res/layout/activity_saved.xml
new file mode 100644
index 00000000..c5b0d296
--- /dev/null
+++ b/app/src/main/res/layout/activity_saved.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_story_viewer.xml b/app/src/main/res/layout/activity_story_viewer.xml
index 23d0593a..d6a6a7f4 100755
--- a/app/src/main/res/layout/activity_story_viewer.xml
+++ b/app/src/main/res/layout/activity_story_viewer.xml
@@ -38,15 +38,42 @@
android:layout_gravity="center"
android:visibility="gone" />
-
+ android:layout_weight="0.3"
+ android:background="#0000"
+ android:weightSum="3"
+ android:layout_gravity="bottom">
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_notification.xml b/app/src/main/res/layout/item_notification.xml
new file mode 100755
index 00000000..77a0f1f3
--- /dev/null
+++ b/app/src/main/res/layout/item_notification.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_changelog_textview.xml b/app/src/main/res/layout/layout_changelog_textview.xml
deleted file mode 100755
index 4083a307..00000000
--- a/app/src/main/res/layout/layout_changelog_textview.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_include_notif_item.xml b/app/src/main/res/layout/layout_include_notif_item.xml
new file mode 100755
index 00000000..a7964c0e
--- /dev/null
+++ b/app/src/main/res/layout/layout_include_notif_item.xml
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_include_toolbar.xml b/app/src/main/res/layout/layout_include_toolbar.xml
index 600dced8..8beaabe3 100755
--- a/app/src/main/res/layout/layout_include_toolbar.xml
+++ b/app/src/main/res/layout/layout_include_toolbar.xml
@@ -6,14 +6,4 @@
android:layout_height="?actionBarSize"
android:background="@null"
app:title="@string/app_name">
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu.xml b/app/src/main/res/menu/menu.xml
index f9b03d5a..bdc259b8 100755
--- a/app/src/main/res/menu/menu.xml
+++ b/app/src/main/res/menu/menu.xml
@@ -12,6 +12,14 @@
android:visible="false"
app:showAsAction="always" />
+
+
-
+
\ No newline at end of file
diff --git a/fastlane/metadata/android/full_description.txt b/fastlane/metadata/android/full_description.txt
index d4cd88f1..9d031c99 100755
--- a/fastlane/metadata/android/full_description.txt
+++ b/fastlane/metadata/android/full_description.txt
@@ -1,12 +1,13 @@
InstaGrabber is an app that allows...
-* Viewing **and downloading** Instagram posts, stories (including highlights)\*, DM\*, and profile pictures, **without** letting people know you viewed it! Works for followed private accounts\*!
+* Viewing **and downloading** Instagram posts (users: timeline & tagged; account feed/saved\*; explore\*), stories (including highlights)\*, DM\*, and profile pictures (HD), **without** letting people know you viewed it! Works for followed private accounts\*!
* Like/bookmark posts\*!
* Downloading multiple posts at once (hold & select)!
* (Un)follow/restrict/block people\*, and (un)follow hashtags\*! (Or you can add shortcuts to them, without logging in!)
* **Copy** post captions, comments, DM messages\*, and profile bios.
+* **Post, Reply, Like, & Delete** comments!\*
* **Compare** follower/following list!
-* Searching usernames and hashtags.
+* Searching usernames, hashtags, and locations!
* Requires [login](https://github.com/austinhuang0131/instagrabber/blob/master/README.md#how-to-log-in). You must be a current follower of the desired private accounts, this app cannot hack people (which I have to state despite the obvious)!
diff --git a/fastlane/metadata/android/images/phoneScreenshots/1.jpg b/fastlane/metadata/android/images/phoneScreenshots/1.jpg
index 5796bf47..8dc68b74 100644
Binary files a/fastlane/metadata/android/images/phoneScreenshots/1.jpg and b/fastlane/metadata/android/images/phoneScreenshots/1.jpg differ
diff --git a/fastlane/metadata/android/images/phoneScreenshots/2.jpg b/fastlane/metadata/android/images/phoneScreenshots/2.jpg
index 36bd581e..b91e6c9d 100644
Binary files a/fastlane/metadata/android/images/phoneScreenshots/2.jpg and b/fastlane/metadata/android/images/phoneScreenshots/2.jpg differ
diff --git a/fastlane/metadata/android/images/phoneScreenshots/3.jpg b/fastlane/metadata/android/images/phoneScreenshots/3.jpg
index ad216f9e..21562700 100644
Binary files a/fastlane/metadata/android/images/phoneScreenshots/3.jpg and b/fastlane/metadata/android/images/phoneScreenshots/3.jpg differ
diff --git a/fastlane/metadata/android/images/phoneScreenshots/4.jpg b/fastlane/metadata/android/images/phoneScreenshots/4.jpg
index b48d64a4..ad216f9e 100644
Binary files a/fastlane/metadata/android/images/phoneScreenshots/4.jpg and b/fastlane/metadata/android/images/phoneScreenshots/4.jpg differ
diff --git a/fastlane/metadata/android/images/phoneScreenshots/5.jpg b/fastlane/metadata/android/images/phoneScreenshots/5.jpg
new file mode 100644
index 00000000..b48d64a4
Binary files /dev/null and b/fastlane/metadata/android/images/phoneScreenshots/5.jpg differ
diff --git a/fastlane/metadata/android/images/phoneScreenshots/6.jpg b/fastlane/metadata/android/images/phoneScreenshots/6.jpg
new file mode 100644
index 00000000..32b6dfe0
Binary files /dev/null and b/fastlane/metadata/android/images/phoneScreenshots/6.jpg differ