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