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!