diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9b389438..ff0a5084 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -186,6 +186,15 @@
android:value=".activities.Main" />
+
+
+
+
+
{
- 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);
}
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/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/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/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/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/dialogs/ProfileSettingsDialog.java b/app/src/main/java/awais/instagrabber/dialogs/ProfileSettingsDialog.java
deleted file mode 100755
index 909227c3..00000000
--- a/app/src/main/java/awais/instagrabber/dialogs/ProfileSettingsDialog.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package awais.instagrabber.dialogs;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.Spinner;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
-
-import awais.instagrabber.R;
-
-import static awais.instagrabber.utils.Constants.PROFILE_FETCH_MODE;
-import static awais.instagrabber.utils.Utils.settingsHelper;
-
-public final class ProfileSettingsDialog extends BottomSheetDialogFragment implements AdapterView.OnItemSelectedListener {
- private int fetchIndex;
- private Activity activity;
- private Spinner spProfileFetchMode;
-
- @NonNull
- @Override
- public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
- final Dialog dialog = super.onCreateDialog(savedInstanceState);
-
- final Context context = getContext();
- activity = context instanceof Activity ? (Activity) context : getActivity();
-
- final View contentView = View.inflate(activity, R.layout.dialog_profile_settings, null);
-
- spProfileFetchMode = contentView.findViewById(R.id.spProfileFetchMode);
-
- fetchIndex = Math.min(2, Math.max(0, settingsHelper.getInteger(PROFILE_FETCH_MODE)));
- spProfileFetchMode.setSelection(fetchIndex);
- spProfileFetchMode.setOnItemSelectedListener(this);
-
- dialog.setContentView(contentView);
-
- return dialog;
- }
-
- @Override
- public void onDismiss(@NonNull final DialogInterface dialog) {
- super.onDismiss(dialog);
- if (activity != null && (spProfileFetchMode == null || fetchIndex != spProfileFetchMode.getSelectedItemPosition()))
- activity.recreate();
- }
-
- @Override
- public void onItemSelected(final AdapterView> 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/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 30e7b4f8..ac94d2cd 100755
--- a/app/src/main/java/awais/instagrabber/utils/Utils.java
+++ b/app/src/main/java/awais/instagrabber/utils/Utils.java
@@ -82,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;
@@ -128,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());
@@ -136,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)
@@ -732,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();
@@ -1189,7 +1200,7 @@ public final class Utils {
if (jsonObject.getString("__typename").equals("GraphTappableFeedMedia") && jsonObject.has("media")) {
storyModels[j].setTappableShortCode(jsonObject.getJSONObject("media").getString(Constants.EXTRAS_SHORTCODE));
}
- else if (jsonObject.optString("__typename").equals("GraphTappableStoryPoll")) {
+ else if (jsonObject.optString("__typename").equals("GraphTappableStoryPoll") && !jsonObject.isNull("id")) {
storyModels[j].setPoll(new PollModel(
jsonObject.getString("id"),
jsonObject.getString("question"),
diff --git a/app/src/main/java/awaisomereport/LogCollector.java b/app/src/main/java/awaisomereport/LogCollector.java
index b0144c87..6ba7c7f2 100755
--- a/app/src/main/java/awaisomereport/LogCollector.java
+++ b/app/src/main/java/awaisomereport/LogCollector.java
@@ -108,6 +108,7 @@ 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"),
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_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_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_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_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/layout/activity_notification.xml b/app/src/main/res/layout/activity_notification.xml
new file mode 100755
index 00000000..f8439a55
--- /dev/null
+++ b/app/src/main/res/layout/activity_notification.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_profile_settings.xml b/app/src/main/res/layout/dialog_profile_settings.xml
deleted file mode 100755
index e8620289..00000000
--- a/app/src/main/res/layout/dialog_profile_settings.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ 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/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" />
+
+
- \|
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values-es/arrays.xml b/app/src/main/res/values-es/arrays.xml
index a9f3f7f7..e3cb9cf1 100755
--- a/app/src/main/res/values-es/arrays.xml
+++ b/app/src/main/res/values-es/arrays.xml
@@ -25,10 +25,6 @@
- \|
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values-fr/arrays.xml b/app/src/main/res/values-fr/arrays.xml
index f473f367..033a4988 100755
--- a/app/src/main/res/values-fr/arrays.xml
+++ b/app/src/main/res/values-fr/arrays.xml
@@ -25,10 +25,6 @@
- \|
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values-in/arrays.xml b/app/src/main/res/values-in/arrays.xml
index 7d038caf..86418b0d 100644
--- a/app/src/main/res/values-in/arrays.xml
+++ b/app/src/main/res/values-in/arrays.xml
@@ -25,10 +25,6 @@
- tanggal
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values-it/arrays.xml b/app/src/main/res/values-it/arrays.xml
index fec122cb..28ac4bfc 100755
--- a/app/src/main/res/values-it/arrays.xml
+++ b/app/src/main/res/values-it/arrays.xml
@@ -25,10 +25,6 @@
- \|
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values-pl/arrays.xml b/app/src/main/res/values-pl/arrays.xml
index 4c25ba6b..03665a79 100644
--- a/app/src/main/res/values-pl/arrays.xml
+++ b/app/src/main/res/values-pl/arrays.xml
@@ -25,10 +25,6 @@
- \|
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values-ru/arrays.xml b/app/src/main/res/values-ru/arrays.xml
index 1f9139a6..f4473185 100644
--- a/app/src/main/res/values-ru/arrays.xml
+++ b/app/src/main/res/values-ru/arrays.xml
@@ -25,10 +25,6 @@
- \|
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values-zh/arrays.xml b/app/src/main/res/values-zh/arrays.xml
index aa91b250..cdf875bb 100755
--- a/app/src/main/res/values-zh/arrays.xml
+++ b/app/src/main/res/values-zh/arrays.xml
@@ -25,10 +25,6 @@
- \|
- -
-
- - Instadp
- - Instafullsize
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 65a09bb6..6c0f9f43 100755
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -28,11 +28,6 @@
- -
-
- - Instadp
- - Instafullsize
-
-
- dd-MM-yyyy
- dd/MM/yyyy
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7dfa1719..258a6835 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4,6 +4,7 @@
The original maintainer, AWAiS, made InstaGrabber as a small and basic little personal app with intentions of [steali-]downloading posts off Instagram. Very unfortunately, this was abandoned and me, Austin Huang, took over the ship. [Let\'s hope that\'s at least a lil\' bit cash money.] After all, this app is fully open source, ad-less, and tracking-less [aside from what Instagram knows]. Even if you don\'t care about downloading stuff [like me], it\'s still a great Instagram client to use!\n\nGot questions [or just wanna talk]? Contact instagrabber@austinhuang.me or click one of the buttons below.
Quick Access
About
+ Notifications
Direct Messages
Settings (v%s)
Settings
@@ -29,6 +30,7 @@
Favorites
Discover
Comments
+ Notifications
Highlight: %s
User Story
Changelog
@@ -60,6 +62,7 @@
Show stories
No more stories!
View Story Post
+ View Post
Spotify
Vote
Vote successful!
@@ -180,6 +183,11 @@
Write a new comment...
+ Liked your post
+ Commented on your post:
+ Started following you
+ Mentioned you:
+
Share this public post to...
This is a private post! Share to those who can view them!