diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b0576c00..7b9d8c27 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -120,13 +120,6 @@
-
-
-
NAV_TO_MENU_ID_MAP = new HashMap<>();
private static final List REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = Collections.singletonList(R.id.commentsViewerFragment);
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";
diff --git a/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java b/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java
deleted file mode 100755
index 2f754167..00000000
--- a/app/src/main/java/awais/instagrabber/activities/NotificationsViewer.java
+++ /dev/null
@@ -1,189 +0,0 @@
-package awais.instagrabber.activities;
-
-import android.content.DialogInterface;
-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.View;
-import android.widget.ArrayAdapter;
-import android.widget.Toast;
-
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import java.io.DataOutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-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.enums.NotificationType;
-import awais.instagrabber.utils.Constants;
-import awais.instagrabber.utils.TextUtils;
-import awais.instagrabber.utils.Utils;
-
-import static awais.instagrabber.utils.Utils.notificationManager;
-
-public final class NotificationsViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
- 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;
- String[] commentDialogList;
-
- @Override
- protected void onCreate(@Nullable final Bundle savedInstanceState) {
- notificationManager.cancel(1800000000);
- if (TextUtils.isEmpty(cookie)) {
- Toast.makeText(this, R.string.activity_notloggedin, Toast.LENGTH_SHORT).show();
- }
- super.onCreate(savedInstanceState);
- notificationsBinding = ActivityNotificationBinding.inflate(getLayoutInflater());
- setContentView(notificationsBinding.getRoot());
- notificationsBinding.swipeRefreshLayout.setOnRefreshListener(this);
- resources = getResources();
- setSupportActionBar(notificationsBinding.toolbar.toolbar);
- notificationsBinding.toolbar.toolbar.setTitle(R.string.action_notif);
- onRefresh();
- }
-
- @Override
- public void onRefresh() {
- notificationsBinding.swipeRefreshLayout.setRefreshing(true);
- new NotificationsFetcher(new FetchListener() {
- @Override
- public void onResult(final NotificationModel[] notificationModels) {
- notificationsBinding.rvComments.setAdapter(new NotificationsAdapter(notificationModels, clickListener, mentionClickListener));
- notificationsBinding.swipeRefreshLayout.setRefreshing(false);
- new SeenAction().execute();
- }
- }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
- if (which == 0)
- searchUsername(notificationModel.getUsername());
- else if (which == 1 && commentDialogList.length == 2) {
- // startActivity(new Intent(getApplicationContext(), PostViewer.class)
- // .putExtra(Constants.EXTRAS_POST, new PostModel(notificationModel.getShortcode(), false)));
- }
- else if (which == 1) new ProfileAction().execute("/approve/");
- else if (which == 2) new ProfileAction().execute("/ignore/");
- };
-
- 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);
-
- if (notificationModel.getShortcode() != null) commentDialogList = new String[]{
- resources.getString(R.string.open_profile),
- resources.getString(R.string.view_post)
- };
- else if (notificationModel.getType() == NotificationType.REQUEST)
- commentDialogList = new String[]{
- resources.getString(R.string.open_profile),
- resources.getString(R.string.request_approve),
- resources.getString(R.string.request_reject)
- };
- 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, isLocation) ->
- 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) {
- // startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text));
- }
-
- class ProfileAction extends AsyncTask {
- boolean ok = false;
- String action;
-
- protected Void doInBackground(String... rawAction) {
- action = rawAction[0];
- final String url = "https://www.instagram.com/web/friendships/"+notificationModel.getId()+action;
- 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",
- Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
- urlConnection.connect();
- if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- ok = true;
- }
- urlConnection.disconnect();
- } catch (Throwable ex) {
- Log.e("austin_debug", action+": " + ex);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if (ok == true) {
- onRefresh();
- }
- else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
- }
- }
-
- class SeenAction extends AsyncTask {
- protected Void doInBackground(Void... lmao) {
- try {
- final HttpURLConnection urlConnection =
- (HttpURLConnection) new URL("https://www.instagram.com/web/activity/mark_checked/").openConnection();
- urlConnection.setRequestMethod("POST");
- urlConnection.setUseCaches(false);
- urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
- urlConnection.setRequestProperty("x-csrftoken",
- Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
- final String urlParameters = "timestamp="+(System.currentTimeMillis()/1000);
- urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- urlConnection.setRequestProperty("Content-Length", "" +
- urlParameters.getBytes().length);
- urlConnection.setDoOutput(true);
- DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
- wr.writeBytes(urlParameters);
- wr.flush();
- wr.close();
- urlConnection.connect();
- } catch (Throwable ex) {
- Log.e("austin_debug", "seen: " + ex);
- }
- return null;
- }
- }
-}
\ 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
old mode 100755
new mode 100644
index b5d9606b..dac5f477
--- a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
+++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
@@ -1,91 +1,55 @@
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.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.ListAdapter;
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.RequestManager;
-import com.bumptech.glide.request.RequestOptions;
-
-import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
+import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.NotificationModel;
-import awais.instagrabber.models.enums.NotificationType;
-public final class NotificationsAdapter extends RecyclerView.Adapter {
- private final View.OnClickListener onClickListener;
+public final class NotificationsAdapter extends ListAdapter {
+ private final OnNotificationClickListener notificationClickListener;
private final MentionClickListener mentionClickListener;
- private final NotificationModel[] notificationModels;
- private LayoutInflater layoutInflater;
- public NotificationsAdapter(final NotificationModel[] notificationModels, final View.OnClickListener onClickListener,
+ private static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() {
+ @Override
+ public boolean areItemsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
+ return oldItem.getId().equals(newItem.getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull final NotificationModel oldItem, @NonNull final NotificationModel newItem) {
+ return oldItem.getId().equals(newItem.getId());
+ }
+ };
+
+ public NotificationsAdapter(final OnNotificationClickListener notificationClickListener,
final MentionClickListener mentionClickListener) {
- this.notificationModels = notificationModels;
- this.onClickListener = onClickListener;
+ super(DIFF_CALLBACK);
+ this.notificationClickListener = notificationClickListener;
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);
+ final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);
+ return new NotificationViewHolder(binding);
}
@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;
- case REQUEST:
- text = R.string.request_notif;
- subtext = notificationModel.getText();
- break;
- }
-
- holder.setCommment(text);
- holder.setSubCommment(subtext);
- if (notificationModel.getType() != NotificationType.REQUEST)
- 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());
- }
+ final NotificationModel notificationModel = getItem(position);
+ holder.bind(notificationModel, notificationClickListener);
}
- @Override
- public int getItemCount() {
- return notificationModels == null ? 0 : notificationModels.length;
+ public interface OnNotificationClickListener {
+ void onNotificationClick(final NotificationModel model);
}
}
\ 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
old mode 100755
new mode 100644
index e660c5e2..fbd527c0
--- a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
+++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
@@ -1,75 +1,68 @@
package awais.instagrabber.adapters.viewholder;
import android.text.Spannable;
+import android.text.TextUtils;
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.customviews.RamboTextView;
-import awais.instagrabber.interfaces.MentionClickListener;
+import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
+import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.NotificationModel;
+import awais.instagrabber.models.enums.NotificationType;
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;
+ private final ItemNotificationBinding binding;
- 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 NotificationViewHolder(final ItemNotificationBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
}
- 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 void bind(final NotificationModel model,
+ final OnNotificationClickListener notificationClickListener) {
+ if (model == null) return;
+ itemView.setOnClickListener(v -> {
+ if (notificationClickListener == null) return;
+ notificationClickListener.onNotificationClick(model);
+ });
+ int text = -1;
+ CharSequence subtext = null;
+ switch (model.getType()) {
+ case LIKE:
+ text = R.string.liked_notif;
+ break;
+ case COMMENT:
+ text = R.string.comment_notif;
+ subtext = model.getText();
+ break;
+ case MENTION:
+ text = R.string.mention_notif;
+ subtext = model.getText();
+ break;
+ case FOLLOW:
+ text = R.string.follow_notif;
+ break;
+ case REQUEST:
+ text = R.string.request_notif;
+ subtext = model.getText();
+ break;
}
- }
-
- 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);
+ binding.tvUsername.setText(model.getUsername());
+ binding.tvComment.setText(text);
+ binding.tvSubComment.setText(subtext, subtext instanceof Spannable ? TextView.BufferType.SPANNABLE : TextView.BufferType.NORMAL);
+ // binding.tvSubComment.setMentionClickListener(mentionClickListener);
+ if (model.getType() != NotificationType.REQUEST) {
+ binding.tvDate.setText(model.getDateTime());
+ }
+ binding.ivProfilePic.setImageURI(model.getProfilePic());
+ if (TextUtils.isEmpty(model.getPreviewPic())) {
+ binding.ivPreviewPic.setVisibility(View.GONE);
+ } else {
+ binding.ivPreviewPic.setVisibility(View.VISIBLE);
+ binding.ivPreviewPic.setImageURI(model.getPreviewPic());
}
}
}
\ 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
old mode 100755
new mode 100644
index 1cf9b4bf..50284647
--- a/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
+++ b/app/src/main/java/awais/instagrabber/asyncs/NotificationsFetcher.java
@@ -8,6 +8,8 @@ import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
@@ -22,16 +24,18 @@ import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
-public final class NotificationsFetcher extends AsyncTask {
- private final FetchListener fetchListener;
+public final class NotificationsFetcher extends AsyncTask> {
+ private static final String TAG = "NotificationsFetcher";
- public NotificationsFetcher(final FetchListener fetchListener) {
+ private final FetchListener> fetchListener;
+
+ public NotificationsFetcher(final FetchListener> fetchListener) {
this.fetchListener = fetchListener;
}
@Override
- protected NotificationModel[] doInBackground(final Void... voids) {
- NotificationModel[] result = null;
+ protected List doInBackground(final Void... voids) {
+ List result = new ArrayList<>();
final String url = "https://www.instagram.com/accounts/activity/?__a=1";
CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE));
@@ -43,37 +47,34 @@ public final class NotificationsFetcher extends AsyncTask 0
&& media.optJSONObject(0).optJSONObject("node") != null) {
- mediaLen = media.length();
- models = new NotificationModel[mediaLen];
- for (int i = 0; i < mediaLen; ++i) {
+ for (int i = 0; i < media.length(); ++i) {
data = media.optJSONObject(i).optJSONObject("node");
if (data == null) continue;
final String type = data.getString("__typename");
final NotificationType notificationType = NotificationType.valueOfType(type);
if (notificationType == null) continue;
- models[i] = new NotificationModel(
+ final JSONObject user = data.getJSONObject("user");
+ result.add(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"),
+ user.getString("id"),
+ user.getString("username"),
+ user.getString("profile_pic_url"),
!data.isNull("media") ? data.getJSONObject("media").getString("shortcode") : null,
- !data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null,
- notificationType);
+ !data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null, notificationType));
}
}
@@ -81,29 +82,27 @@ public final class NotificationsFetcher extends AsyncTask 0
&& media.optJSONObject(0).optJSONObject("node") != null) {
- reqLen = media.length();
- req = new NotificationModel[reqLen];
- for (int i = 0; i < reqLen; ++i) {
+ for (int i = 0; i < media.length(); ++i) {
data = media.optJSONObject(i).optJSONObject("node");
if (data == null) continue;
- req[i] = new NotificationModel(data.getString(Constants.EXTRAS_ID),
- data.optString("full_name"), 0L, data.getString("username"),
- data.getString("profile_pic_url"), null, null, NotificationType.REQUEST);
+ result.add(new NotificationModel(
+ data.getString(Constants.EXTRAS_ID),
+ data.optString("full_name"),
+ 0L,
+ data.getString(Constants.EXTRAS_ID),
+ data.getString("username"),
+ data.getString("profile_pic_url"),
+ null,
+ null, NotificationType.REQUEST));
}
}
-
- result = new NotificationModel[mediaLen + reqLen];
- if (req != null) System.arraycopy(req, 0, result, 0, reqLen);
- if (models != null) System.arraycopy(models, 0, result, reqLen, mediaLen);
}
-
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);
+ if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
-
return result;
}
@@ -113,7 +112,7 @@ public final class NotificationsFetcher extends AsyncTask result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
new file mode 100644
index 00000000..9db22df9
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
@@ -0,0 +1,215 @@
+package awais.instagrabber.fragments;
+
+import android.content.DialogInterface;
+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.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.navigation.NavDirections;
+import androidx.navigation.fragment.NavHostFragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import awais.instagrabber.R;
+import awais.instagrabber.adapters.NotificationsAdapter;
+import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
+import awais.instagrabber.asyncs.NotificationsFetcher;
+import awais.instagrabber.databinding.FragmentNotificationsViewerBinding;
+import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
+import awais.instagrabber.interfaces.MentionClickListener;
+import awais.instagrabber.models.enums.NotificationType;
+import awais.instagrabber.repositories.responses.FriendshipRepoChangeRootResponse;
+import awais.instagrabber.services.FriendshipService;
+import awais.instagrabber.services.NewsService;
+import awais.instagrabber.services.ServiceCallback;
+import awais.instagrabber.utils.Constants;
+import awais.instagrabber.utils.CookieUtils;
+import awais.instagrabber.utils.TextUtils;
+import awais.instagrabber.utils.Utils;
+import awais.instagrabber.viewmodels.NotificationViewModel;
+
+import static awais.instagrabber.utils.Utils.notificationManager;
+
+public final class NotificationsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
+ private static final String TAG = "NotificationsViewer";
+
+ private FragmentNotificationsViewerBinding binding;
+ private SwipeRefreshLayout root;
+ private boolean shouldRefresh = true;
+ private NotificationViewModel notificationViewModel;
+ private FriendshipService friendshipService;
+ private String userId;
+ private String csrfToken;
+ private NewsService newsService;
+
+ private final OnNotificationClickListener clickListener = model -> {
+ if (model == null) return;
+ final String username = model.getUsername();
+ final SpannableString title = new SpannableString(username + (TextUtils.isEmpty(model.getText()) ? "" : (":\n" + model.getText())));
+ title.setSpan(new RelativeSizeSpan(1.23f), 0, username.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ String[] commentDialogList;
+ if (model.getShortCode() != null) {
+ commentDialogList = new String[]{
+ getString(R.string.open_profile),
+ getString(R.string.view_post)
+ };
+ } else if (model.getType() == NotificationType.REQUEST) {
+ commentDialogList = new String[]{
+ getString(R.string.open_profile),
+ getString(R.string.request_approve),
+ getString(R.string.request_reject)
+ };
+ } else {
+ commentDialogList = new String[]{getString(R.string.open_profile)};
+ }
+ if (getContext() == null) return;
+ final DialogInterface.OnClickListener profileDialogListener = (dialog, which) -> {
+ switch (which) {
+ case 0:
+ openProfile(model.getUsername());
+ break;
+ case 1:
+ if (model.getType() == NotificationType.REQUEST) {
+ friendshipService.approve(userId, model.getUserId(), csrfToken, new ServiceCallback() {
+ @Override
+ public void onSuccess(final FriendshipRepoChangeRootResponse result) {
+ // Log.d(TAG, "onSuccess: " + result);
+ if (result.getStatus().equals("ok")) {
+ onRefresh();
+ return;
+ }
+ Log.e(TAG, "approve: status was not ok!");
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "approve: onFailure: ", t);
+ }
+ });
+ return;
+ }
+ final NavDirections action = MorePreferencesFragmentDirections
+ .actionGlobalPostViewFragment(0, new String[]{model.getShortCode()}, false);
+ NavHostFragment.findNavController(this).navigate(action);
+ break;
+ case 2:
+ friendshipService.ignore(userId, model.getUserId(), csrfToken, new ServiceCallback() {
+ @Override
+ public void onSuccess(final FriendshipRepoChangeRootResponse result) {
+ // Log.d(TAG, "onSuccess: " + result);
+ if (result.getStatus().equals("ok")) {
+ onRefresh();
+ return;
+ }
+ Log.e(TAG, "ignore: status was not ok!");
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "ignore: onFailure: ", t);
+ }
+ });
+ break;
+ }
+ };
+ new AlertDialog.Builder(getContext())
+ .setTitle(title)
+ .setItems(commentDialogList, profileDialogListener)
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ };
+ private final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> {
+ if (getContext() == null) return;
+ new AlertDialog.Builder(getContext())
+ .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) -> openProfile(text))
+ .show();
+ };
+
+ @Override
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ notificationManager.cancel(Constants.ACTIVITY_NOTIFICATION_ID);
+ final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
+ if (TextUtils.isEmpty(cookie)) {
+ Toast.makeText(getContext(), R.string.activity_notloggedin, Toast.LENGTH_SHORT).show();
+ }
+ friendshipService = FriendshipService.getInstance();
+ newsService = NewsService.getInstance();
+ userId = CookieUtils.getUserIdFromCookie(cookie);
+ csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
+ }
+
+ @NonNull
+ @Override
+ public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
+ if (root != null) {
+ shouldRefresh = false;
+ return root;
+ }
+ binding = FragmentNotificationsViewerBinding.inflate(getLayoutInflater());
+ root = binding.getRoot();
+ return root;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
+ if (!shouldRefresh) return;
+ init();
+ shouldRefresh = false;
+ }
+
+ private void init() {
+ binding.swipeRefreshLayout.setOnRefreshListener(this);
+ notificationViewModel = new ViewModelProvider(this).get(NotificationViewModel.class);
+ final NotificationsAdapter adapter = new NotificationsAdapter(clickListener, mentionClickListener);
+ binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext()));
+ binding.rvComments.setAdapter(adapter);
+ notificationViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
+ onRefresh();
+ }
+
+ @Override
+ public void onRefresh() {
+ binding.swipeRefreshLayout.setRefreshing(true);
+ new NotificationsFetcher(notificationModels -> {
+ binding.swipeRefreshLayout.setRefreshing(false);
+ notificationViewModel.getList().postValue(notificationModels);
+ final String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+ newsService.markChecked(timestamp, csrfToken, new ServiceCallback() {
+ @Override
+ public void onSuccess(@NonNull final Boolean result) {
+ // Log.d(TAG, "onResponse: body: " + result);
+ if (!result) Log.e(TAG, "onSuccess: Error marking activity checked, response is false");
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ Log.e(TAG, "onFailure: Error marking activity checked", t);
+ }
+ });
+ }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private void openProfile(final String username) {
+ final NavDirections action = MorePreferencesFragmentDirections
+ .actionGlobalProfileFragment("@" + username);
+ NavHostFragment.findNavController(this).navigate(action);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java b/app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java
index f68df3b0..405bcfee 100644
--- a/app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/PostViewFragment.java
@@ -291,6 +291,7 @@ public class PostViewFragment extends Fragment {
return;
}
}
+ if (currentPostIndex >= idOrCodeList.size() || currentPostIndex < 0) return;
final String idOrShortCode = idOrCodeList.get(currentPostIndex);
if (isId) {
new iPostFetcher(idOrShortCode, pfl).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
index 797e0bcd..36419e5b 100644
--- a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
+++ b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
@@ -100,7 +100,11 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
generalCategory.setTitle("General");
generalCategory.setIconSpaceReserved(false);
screen.addPreference(generalCategory);
- generalCategory.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> false));
+ generalCategory.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
+ final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToNotificationsViewer();
+ NavHostFragment.findNavController(this).navigate(navDirections);
+ return true;
+ }));
generalCategory.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToSettingsPreferencesFragment();
NavHostFragment.findNavController(this).navigate(navDirections);
diff --git a/app/src/main/java/awais/instagrabber/models/NotificationModel.java b/app/src/main/java/awais/instagrabber/models/NotificationModel.java
index 850d184a..d461a033 100755
--- a/app/src/main/java/awais/instagrabber/models/NotificationModel.java
+++ b/app/src/main/java/awais/instagrabber/models/NotificationModel.java
@@ -9,19 +9,32 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public final class NotificationModel {
- private final String id, username, profilePicUrl, shortcode, previewUrl;
+ private final String id;
+ private final String userId;
+ private final String username;
+ private final String profilePicUrl;
+ private final String shortCode;
+ private final String 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) {
+ public NotificationModel(final String id,
+ final String text,
+ final long timestamp,
+ final String userId,
+ final String username,
+ final String profilePicUrl,
+ final String shortCode,
+ final String previewUrl,
+ final NotificationType type) {
this.id = id;
this.text = TextUtils.hasMentions(text) ? TextUtils.getMentionText(text) : text;
this.timestamp = timestamp;
+ this.userId = userId;
this.username = username;
this.profilePicUrl = profilePicUrl;
- this.shortcode = shortcode;
+ this.shortCode = shortCode;
this.previewUrl = previewUrl;
this.type = type;
}
@@ -39,6 +52,10 @@ public final class NotificationModel {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
}
+ public String getUserId() {
+ return userId;
+ }
+
public String getUsername() {
return username;
}
@@ -47,8 +64,8 @@ public final class NotificationModel {
return profilePicUrl;
}
- public String getShortcode() {
- return shortcode;
+ public String getShortCode() {
+ return shortCode;
}
public String getPreviewPic() {
diff --git a/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java
new file mode 100644
index 00000000..e8e71116
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/repositories/NewsRepository.java
@@ -0,0 +1,21 @@
+package awais.instagrabber.repositories;
+
+import java.util.Map;
+
+import awais.instagrabber.utils.Constants;
+import retrofit2.Call;
+import retrofit2.http.FieldMap;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.Header;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+public interface NewsRepository {
+
+ Call inbox();
+
+ @FormUrlEncoded
+ @Headers("User-Agent: " + Constants.USER_AGENT)
+ @POST("https://www.instagram.com/web/activity/mark_checked/")
+ Call markChecked(@Header("x-csrftoken") String csrfToken, @FieldMap Map map);
+}
diff --git a/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java b/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java
index 38709e09..6b6d04bb 100644
--- a/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java
+++ b/app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java
@@ -17,8 +17,11 @@ public class AddCookiesInterceptor implements Interceptor {
final Request request = chain.request();
final Request.Builder builder = request.newBuilder();
final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
- builder.addHeader("Cookie", cookie)
- .addHeader("User-Agent", Constants.I_USER_AGENT);
+ builder.addHeader("Cookie", cookie);
+ final String userAgentHeader = "User-Agent";
+ if (request.header(userAgentHeader) == null) {
+ builder.addHeader(userAgentHeader, Constants.I_USER_AGENT);
+ }
final Request updatedRequest = builder.build();
return chain.proceed(updatedRequest);
}
diff --git a/app/src/main/java/awais/instagrabber/services/BaseService.java b/app/src/main/java/awais/instagrabber/services/BaseService.java
index 6347ed0c..1f78a22d 100644
--- a/app/src/main/java/awais/instagrabber/services/BaseService.java
+++ b/app/src/main/java/awais/instagrabber/services/BaseService.java
@@ -19,8 +19,8 @@ public abstract class BaseService {
if (builder == null) {
final OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
- .followRedirects(true)
- .followSslRedirects(true);
+ .followRedirects(false)
+ .followSslRedirects(false);
if (BuildConfig.DEBUG) {
// clientBuilder.addInterceptor(new LoggingInterceptor());
}
diff --git a/app/src/main/java/awais/instagrabber/services/FriendshipService.java b/app/src/main/java/awais/instagrabber/services/FriendshipService.java
index c3b500d7..f6ec3b3a 100644
--- a/app/src/main/java/awais/instagrabber/services/FriendshipService.java
+++ b/app/src/main/java/awais/instagrabber/services/FriendshipService.java
@@ -99,6 +99,20 @@ public class FriendshipService extends BaseService {
});
}
+ public void approve(final String userId,
+ final String targetUserId,
+ final String crsfToken,
+ final ServiceCallback callback) {
+ change("approve", userId, targetUserId, crsfToken, callback);
+ }
+
+ public void ignore(final String userId,
+ final String targetUserId,
+ final String crsfToken,
+ final ServiceCallback callback) {
+ change("ignore", userId, targetUserId, crsfToken, callback);
+ }
+
private void change(final String action,
final String userId,
final String targetUserId,
diff --git a/app/src/main/java/awais/instagrabber/services/NewsService.java b/app/src/main/java/awais/instagrabber/services/NewsService.java
new file mode 100644
index 00000000..d8acc607
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/services/NewsService.java
@@ -0,0 +1,69 @@
+package awais.instagrabber.services;
+
+import androidx.annotation.NonNull;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import awais.instagrabber.repositories.NewsRepository;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+public class NewsService extends BaseService {
+ private static final String TAG = "NewsService";
+
+ private final NewsRepository repository;
+
+ private static NewsService instance;
+
+ private NewsService() {
+ final Retrofit retrofit = getRetrofitBuilder()
+ .baseUrl("https://i.instagram.com")
+ .build();
+ repository = retrofit.create(NewsRepository.class);
+ }
+
+ public static NewsService getInstance() {
+ if (instance == null) {
+ instance = new NewsService();
+ }
+ return instance;
+ }
+
+ public void markChecked(final String timestamp,
+ final String csrfToken,
+ final ServiceCallback callback) {
+ final Map map = new HashMap<>();
+ map.put("timestamp", timestamp);
+ final Call request = repository.markChecked(csrfToken, map);
+ request.enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull final Call call, @NonNull final Response response) {
+ final String body = response.body();
+ if (body == null) {
+ callback.onSuccess(false);
+ return;
+ }
+ try {
+ final JSONObject jsonObject = new JSONObject(body);
+ final String status = jsonObject.optString("status");
+ callback.onSuccess(status.equals("ok"));
+
+ } catch (JSONException e) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull final Call call, @NonNull final Throwable t) {
+ callback.onFailure(t);
+ // Log.e(TAG, "onFailure: ", t);
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java
old mode 100755
new mode 100644
index 158ebd8f..df57564a
--- a/app/src/main/java/awais/instagrabber/utils/Constants.java
+++ b/app/src/main/java/awais/instagrabber/utils/Constants.java
@@ -74,4 +74,5 @@ public final class Constants {
public static final String CHANNEL_ID = "InstaGrabber";
public static final String CHANNEL_NAME = "Instagrabber";
public static final String NOTIF_GROUP_NAME = "awais.instagrabber.InstaNotif";
+ public static final int ACTIVITY_NOTIFICATION_ID = 1800000000;
}
\ No newline at end of file
diff --git a/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java
new file mode 100644
index 00000000..aa8fc194
--- /dev/null
+++ b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java
@@ -0,0 +1,19 @@
+package awais.instagrabber.viewmodels;
+
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import java.util.List;
+
+import awais.instagrabber.models.NotificationModel;
+
+public class NotificationViewModel extends ViewModel {
+ private MutableLiveData> list;
+
+ public MutableLiveData> getList() {
+ if (list == null) {
+ list = new MutableLiveData<>();
+ }
+ return list;
+ }
+}
diff --git a/app/src/main/res/layout/activity_notification.xml b/app/src/main/res/layout/activity_notification.xml
deleted file mode 100755
index f8439a55..00000000
--- a/app/src/main/res/layout/activity_notification.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_notifications_viewer.xml b/app/src/main/res/layout/fragment_notifications_viewer.xml
new file mode 100644
index 00000000..dd3cda20
--- /dev/null
+++ b/app/src/main/res/layout/fragment_notifications_viewer.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
\ 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
old mode 100755
new mode 100644
index e65bfcdc..f2c344f1
--- a/app/src/main/res/layout/item_notification.xml
+++ b/app/src/main/res/layout/item_notification.xml
@@ -1,19 +1,192 @@
-
+ android:background="?android:selectableItemBackground"
+ android:baselineAligned="false"
+ android:padding="8dp">
-
+
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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
deleted file mode 100755
index 828e2547..00000000
--- a/app/src/main/res/layout/layout_include_notif_item.xml
+++ /dev/null
@@ -1,152 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/navigation/more_nav_graph.xml b/app/src/main/res/navigation/more_nav_graph.xml
index f025d67e..1efae0f1 100644
--- a/app/src/main/res/navigation/more_nav_graph.xml
+++ b/app/src/main/res/navigation/more_nav_graph.xml
@@ -1,9 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
old mode 100755
new mode 100644
index 65515f29..3029893c
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -15,7 +15,9 @@
80dp
35dp
+ 40dp
+ 56dp
@dimen/simple_item_picture_size
@dimen/feed_profile_size
500dp