Add activity/notification view fragment

This commit is contained in:
Ammar Githam 2020-09-11 22:27:09 +09:00
parent 73a9e627d9
commit 37912854d0
23 changed files with 729 additions and 558 deletions

View File

@ -120,13 +120,6 @@
<data android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".activities.NotificationsViewer"
android:parentActivityName=".activities.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity
android:name=".activities.Login"
android:label="@string/login"

View File

@ -78,7 +78,8 @@ public class MainActivity extends BaseLanguageActivity {
R.id.savedViewerFragment,
R.id.commentsViewerFragment,
R.id.followViewerFragment,
R.id.directMessagesSettingsFragment);
R.id.directMessagesSettingsFragment,
R.id.notificationsViewer);
private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>();
private static final List<Integer> REMOVE_COLLAPSING_TOOLBAR_SCROLL_DESTINATIONS = Collections.singletonList(R.id.commentsViewerFragment);
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";

View File

@ -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<String> 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<NotificationModel[]>() {
@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<String, Void, Void> {
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<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
try {
final HttpURLConnection urlConnection =
(HttpURLConnection) new URL("https://www.instagram.com/web/activity/mark_checked/").openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
urlConnection.setRequestProperty("x-csrftoken",
Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
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;
}
}
}

View File

@ -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<NotificationViewHolder> {
private final View.OnClickListener onClickListener;
public final class NotificationsAdapter extends ListAdapter<NotificationModel, NotificationViewHolder> {
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<NotificationModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<NotificationModel>() {
@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);
}
}

View File

@ -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());
}
}
}

View File

@ -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<Void, Void, NotificationModel[]> {
private final FetchListener<NotificationModel[]> fetchListener;
public final class NotificationsFetcher extends AsyncTask<Void, Void, List<NotificationModel>> {
private static final String TAG = "NotificationsFetcher";
public NotificationsFetcher(final FetchListener<NotificationModel[]> fetchListener) {
private final FetchListener<List<NotificationModel>> fetchListener;
public NotificationsFetcher(final FetchListener<List<NotificationModel>> fetchListener) {
this.fetchListener = fetchListener;
}
@Override
protected NotificationModel[] doInBackground(final Void... voids) {
NotificationModel[] result = null;
protected List<NotificationModel> doInBackground(final Void... voids) {
List<NotificationModel> 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<Void, Void, Notificati
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
JSONObject page = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql").getJSONObject("user"),
ewaf = page.getJSONObject("activity_feed").optJSONObject("edge_web_activity_feed"),
efr = page.optJSONObject("edge_follow_requests"),
data;
final JSONObject page = new JSONObject(NetworkUtils.readFromConnection(conn))
.getJSONObject("graphql")
.getJSONObject("user");
final JSONObject ewaf = page.getJSONObject("activity_feed")
.optJSONObject("edge_web_activity_feed");
final JSONObject efr = page.optJSONObject("edge_follow_requests");
JSONObject data;
JSONArray media;
// int totalLength = 0;
int mediaLen = 0;
int reqLen = 0;
NotificationModel[] models = null, req = null;
if (ewaf != null
&& (media = ewaf.optJSONArray("edges")) != null
&& media.length() > 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<Void, Void, Notificati
&& (media = efr.optJSONArray("edges")) != null
&& media.length() > 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<Void, Void, Notificati
}
@Override
protected void onPostExecute(final NotificationModel[] result) {
protected void onPostExecute(final List<NotificationModel> result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -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<FriendshipRepoChangeRootResponse>() {
@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<FriendshipRepoChangeRootResponse>() {
@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<Boolean>() {
@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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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() {

View File

@ -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<String> inbox();
@FormUrlEncoded
@Headers("User-Agent: " + Constants.USER_AGENT)
@POST("https://www.instagram.com/web/activity/mark_checked/")
Call<String> markChecked(@Header("x-csrftoken") String csrfToken, @FieldMap Map<String, String> map);
}

View File

@ -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);
}

View File

@ -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());
}

View File

@ -99,6 +99,20 @@ public class FriendshipService extends BaseService {
});
}
public void approve(final String userId,
final String targetUserId,
final String crsfToken,
final ServiceCallback<FriendshipRepoChangeRootResponse> callback) {
change("approve", userId, targetUserId, crsfToken, callback);
}
public void ignore(final String userId,
final String targetUserId,
final String crsfToken,
final ServiceCallback<FriendshipRepoChangeRootResponse> callback) {
change("ignore", userId, targetUserId, crsfToken, callback);
}
private void change(final String action,
final String userId,
final String targetUserId,

View File

@ -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<Boolean> callback) {
final Map<String, String> map = new HashMap<>();
map.put("timestamp", timestamp);
final Call<String> request = repository.markChecked(csrfToken, map);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> 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<String> call, @NonNull final Throwable t) {
callback.onFailure(t);
// Log.e(TAG, "onFailure: ", t);
}
});
}
}

View File

@ -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;
}

View File

@ -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<NotificationModel>> list;
public MutableLiveData<List<NotificationModel>> getList() {
if (list == null) {
list = new MutableLiveData<>();
}
return list;
}
}

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activities.NotificationsViewer">
<include
android:id="@+id/toolbar"
layout="@layout/layout_include_toolbar" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvComments"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
app:layoutManager="LinearLayoutManager"
tools:listitem="@layout/item_notification" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.NotificationsViewerFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvComments"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/item_notification" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

199
app/src/main/res/layout/item_notification.xml Executable file → Normal file
View File

@ -1,19 +1,192 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:viewBindingIgnore="true">
android:background="?android:selectableItemBackground"
android:baselineAligned="false"
android:padding="8dp">
<include
android:id="@+id/container"
layout="@layout/layout_include_notif_item" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/ivProfilePic"
android:layout_width="@dimen/notification_image_size"
android:layout_height="@dimen/notification_image_size"
android:visibility="visible"
app:actualImageScaleType="centerCrop"
app:layout_constraintEnd_toStartOf="@id/barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true"
tools:placeholderImage="@mipmap/ic_launcher" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginBottom="4dp"
android:background="#80888888" />
</LinearLayout>
<!--<com.facebook.drawee.view.SimpleDraweeView-->
<!-- android:id="@+id/multi_pic1"-->
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
<!-- app:actualImageScaleType="centerCrop"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
<!--<com.facebook.drawee.view.SimpleDraweeView-->
<!-- android:id="@+id/multi_pic2"-->
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
<!-- app:layout_constraintBottom_toTopOf="@+id/multi_pic3"-->
<!-- app:layout_constraintEnd_toStartOf="@id/barrier"-->
<!-- app:layout_constraintStart_toEndOf="@id/multi_pic1"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
<!--<com.facebook.drawee.view.SimpleDraweeView-->
<!-- android:id="@+id/multi_pic3"-->
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintEnd_toStartOf="@id/barrier"-->
<!-- app:layout_constraintStart_toEndOf="@id/multi_pic1"-->
<!-- app:layout_constraintTop_toBottomOf="@+id/multi_pic2"-->
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="ivProfilePic" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:paddingStart="16dp"
android:paddingLeft="16dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/tvComment"
app:layout_constraintEnd_toStartOf="@id/ivPreviewPic"
app:layout_constraintStart_toStartOf="@id/barrier"
app:layout_constraintTop_toTopOf="parent"
tools:text="username" />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/tvComment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:linksClickable="true"
android:paddingStart="16dp"
android:paddingLeft="16dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:textAppearance="@style/TextAppearance.AppCompat"
android:textStyle="italic"
app:layout_constraintBottom_toTopOf="@id/tvSubComment"
app:layout_constraintEnd_toStartOf="@id/ivPreviewPic"
app:layout_constraintStart_toStartOf="@id/tvUsername"
app:layout_constraintTop_toBottomOf="@id/tvUsername"
tools:text="comment comment comment comment comment comment comment comment comment comment comment comment comment " />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/tvSubComment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autoLink="web|email"
android:ellipsize="end"
android:linksClickable="true"
android:paddingStart="16dp"
android:paddingLeft="16dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintEnd_toStartOf="@id/tvDate"
app:layout_constraintStart_toStartOf="@id/tvUsername"
app:layout_constraintTop_toBottomOf="@id/tvComment"
tools:text="sub-comment" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:gravity="end"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:textStyle="italic"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ivPreviewPic"
app:layout_constraintStart_toEndOf="@id/tvSubComment"
app:layout_constraintTop_toBottomOf="@id/tvComment"
tools:text="date" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/preview_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="ivPreviewPic" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/ivPreviewPic"
android:layout_width="@dimen/notification_image_size"
android:layout_height="@dimen/notification_image_size"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/preview_barrier"
app:layout_constraintTop_toTopOf="parent"
tools:placeholderImage="@mipmap/ic_launcher"
tools:visibility="visible" />
<!--<com.facebook.drawee.view.SimpleDraweeView-->
<!-- android:id="@+id/preview_pic1"-->
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintEnd_toStartOf="@id/preview_pic2"-->
<!-- app:layout_constraintStart_toEndOf="@id/preview_barrier"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
<!--<com.facebook.drawee.view.SimpleDraweeView-->
<!-- android:id="@+id/preview_pic2"-->
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toEndOf="@id/preview_pic1"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
<!--<com.facebook.drawee.view.SimpleDraweeView-->
<!-- android:id="@+id/preview_pic3"-->
<!-- android:layout_width="@dimen/simple_item_picture_size_exact_half"-->
<!-- android:layout_height="@dimen/simple_item_picture_size_exact_half"-->
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toEndOf="@id/preview_pic1"-->
<!-- app:layout_constraintTop_toBottomOf="@id/preview_pic2"-->
<!-- app:placeholderImage="@mipmap/ic_launcher" />-->
<!--<androidx.constraintlayout.widget.Group-->
<!-- android:id="@+id/multi_pic_group"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:visibility="visible"-->
<!-- app:constraint_referenced_ids="multi_pic1, multi_pic2, multi_pic3" />-->
<!--<androidx.constraintlayout.widget.Group-->
<!-- android:id="@+id/preview_pic_group"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:visibility="gone"-->
<!-- app:constraint_referenced_ids="preview_pic1, preview_pic2, preview_pic3" />-->
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,152 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:paddingStart="0dp"
android:paddingLeft="0dp"
android:paddingEnd="4dp"
android:paddingRight="4dp">
<FrameLayout
android:layout_width="@dimen/simple_item_picture_size"
android:layout_height="@dimen/simple_item_picture_size"
android:gravity="center">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivProfilePic"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_weight="1.0"
android:ellipsize="marquee"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingTop="4dp"
android:paddingEnd="4dp"
android:paddingRight="4dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingTop="4dp"
android:paddingRight="8dp">
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/tvComment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:ellipsize="end"
android:linksClickable="true"
android:textStyle="italic"
android:textAppearance="@style/TextAppearance.AppCompat" />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/tvSubComment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:autoLink="web|email"
android:ellipsize="end"
android:linksClickable="true"
android:textAppearance="@style/TextAppearance.AppCompat" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:ellipsize="marquee"
android:gravity="right"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:singleLine="true"
android:textStyle="italic" />
</LinearLayout>
<FrameLayout
android:layout_width="@dimen/simple_item_picture_size"
android:layout_height="@dimen/simple_item_picture_size"
android:gravity="center">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivPreviewPic"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/rightContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</LinearLayout>

View File

@ -1,9 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/more_nav_graph"
app:startDestination="@id/morePreferencesFragment">
<include app:graph="@navigation/post_view_nav_graph" />
<include app:graph="@navigation/profile_nav_graph" />
<include app:graph="@navigation/hashtag_nav_graph" />
<include app:graph="@navigation/location_nav_graph" />
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_postViewFragment"
app:destination="@id/post_view_nav_graph">
<argument
android:name="index"
app:argType="integer" />
<argument
android:name="idOrCodeArray"
app:argType="string[]" />
<argument
android:name="isId"
app:argType="boolean" />
</action>
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<fragment
android:id="@+id/morePreferencesFragment"
android:name="awais.instagrabber.fragments.settings.MorePreferencesFragment"
@ -14,6 +44,9 @@
<action
android:id="@+id/action_morePreferencesFragment_to_aboutFragment"
app:destination="@id/aboutFragment" />
<action
android:id="@+id/action_morePreferencesFragment_to_notificationsViewer"
app:destination="@id/notificationsViewer" />
</fragment>
<fragment
android:id="@+id/settingsPreferencesFragment"
@ -23,4 +56,9 @@
android:id="@+id/aboutFragment"
android:name="awais.instagrabber.fragments.settings.AboutFragment"
android:label="@string/action_about" />
<fragment
android:id="@+id/notificationsViewer"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer" />
</navigation>

2
app/src/main/res/values/dimens.xml Executable file → Normal file
View File

@ -15,7 +15,9 @@
<dimen name="simple_item_picture_size">80dp</dimen>
<dimen name="simple_item_picture_size_half">35dp</dimen>
<dimen name="simple_item_picture_size_exact_half">40dp</dimen>
<dimen name="notification_image_size">56dp</dimen>
<dimen name="message_item_size">@dimen/simple_item_picture_size</dimen>
<dimen name="message_item_profile_size">@dimen/feed_profile_size</dimen>
<dimen name="dm_media_img_max_height">500dp</dimen>