1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 14:47:29 +00:00

Tabs! Fragments! WIP!

This commit is contained in:
Ammar Githam 2020-08-29 17:01:42 +09:00
parent e7ed3eb249
commit 3f6c74d671
101 changed files with 5734 additions and 2201 deletions

View File

@ -37,21 +37,28 @@ android {
}
dependencies {
implementation('androidx.appcompat:appcompat:1.3.0-alpha01@aar') { transitive true }
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation('com.google.android.material:material:1.3.0-alpha02@aar') { transitive true }
implementation('androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01') { transitive true }
def appcompat_version = "1.2.0"
def nav_version = "2.3.0"
implementation "androidx.appcompat:appcompat:$appcompat_version"
// For loading and tinting drawables on older versions of the platform
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05"
implementation 'com.google.android.material:material:1.2.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.constraintlayout:constraintlayout:2.0.0"
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.github.chrisbanes:PhotoView:v2.0.0'
implementation 'com.google.android.exoplayer:exoplayer:2.11.1'
implementation 'com.facebook.fresco:fresco:2.3.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
implementation('org.jsoup:jsoup:1.13.1') { transitive true }
implementation('com.github.bumptech.glide:glide:4.11.0') { transitive true }
implementation('com.github.chrisbanes:PhotoView:v2.0.0@aar') { transitive true }
implementation('com.google.android.exoplayer:exoplayer:2.11.1@aar') { transitive true }
implementation 'com.facebook.fresco:fresco:2.3.0'
}

View File

@ -51,13 +51,11 @@ import java.util.Map;
import awais.instagrabber.activities.CommentsViewer;
import awais.instagrabber.activities.FollowViewer;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.activities.MainActivityBackup;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.activities.SavedViewer;
import awais.instagrabber.activities.StoryViewer;
import awais.instagrabber.adapters.DiscoverAdapter;
import awais.instagrabber.adapters.FeedAdapter;
import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.adapters.PostsAdapter;
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
import awais.instagrabber.asyncs.DiscoverFetcher;
@ -111,8 +109,14 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
private static AsyncTask<?, ?, ?> currentlyExecuting;
private AsyncTask<Void, Void, FeedStoryModel[]> prevStoriesFetcher;
private FeedStoryModel[] stories;
private boolean hasNextPage = false, feedHasNextPage = false, discoverHasMore = false;
private String endCursor = null, feedEndCursor = null, discoverEndMaxId = null, topic = null, rankToken = null;
private boolean hasNextPage = false;
private boolean feedHasNextPage = false;
private boolean discoverHasMore = false;
private String endCursor = null;
private String feedEndCursor = null;
private String discoverEndMaxId = null;
private String topic = null;
private String rankToken = null;
private String[] topicIds = null;
private final boolean autoloadPosts;
@ -293,7 +297,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
@Override
public void onResult(final FeedStoryModel[] result) {
feedStoriesAdapter.setData(result);
// feedStoriesAdapter.setData(result);
if (result != null && result.length > 0) {
mainActivity.mainBinding.feedView.feedStories.setVisibility(View.VISIBLE);
stories = result;
@ -305,32 +309,32 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
public void onClick(final RamboTextView view, final String text, final boolean isHashtag) {
new AlertDialog.Builder(mainActivity).setMessage(isHashtag ? R.string.comment_view_mention_hash_search : R.string.comment_view_mention_user_search)
.setTitle(text).setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok, (dialog, which) -> {
if (MainActivity.scanHack != null) MainActivity.scanHack.onResult(text);
if (MainActivityBackup.scanHack != null) MainActivityBackup.scanHack.onResult(text);
}).show();
}
};
private final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter(null, new View.OnClickListener() {
@Override
public void onClick(final View v) {
final Object tag = v.getTag();
if (tag instanceof FeedStoryModel) {
final FeedStoryModel feedStoryModel = (FeedStoryModel) tag;
final int index = indexOfIntArray(stories, feedStoryModel);
new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> {
if (result != null && result.length > 0)
mainActivity.startActivity(new Intent(mainActivity, StoryViewer.class)
.putExtra(Constants.EXTRAS_STORIES, result)
.putExtra(Constants.EXTRAS_USERNAME, feedStoryModel.getProfileModel().getUsername())
.putExtra(Constants.FEED, stories)
.putExtra(Constants.FEED_ORDER, index)
);
else
Toast.makeText(mainActivity, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
private MainActivity mainActivity;
// private final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter(null, new View.OnClickListener() {
// @Override
// public void onClick(final View v) {
// final Object tag = v.getTag();
// if (tag instanceof FeedStoryModel) {
// final FeedStoryModel feedStoryModel = (FeedStoryModel) tag;
// final int index = indexOfIntArray(stories, feedStoryModel);
// new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> {
// if (result != null && result.length > 0)
// mainActivity.startActivity(new Intent(mainActivity, StoryViewer.class)
// .putExtra(Constants.EXTRAS_STORIES, result)
// .putExtra(Constants.EXTRAS_USERNAME, feedStoryModel.getProfileModel().getUsername())
// .putExtra(Constants.FEED, stories)
// .putExtra(Constants.FEED_ORDER, index)
// );
// else
// Toast.makeText(mainActivity, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
// }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// }
// }
// });
private MainActivityBackup mainActivity;
private Resources resources;
private final View collapsingToolbar;
private final RecyclerLazyLoader lazyLoader;
@ -345,7 +349,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
private RequestManager glide;
private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
public MainHelper(@NonNull final MainActivity mainActivity) {
public MainHelper(@NonNull final MainActivityBackup mainActivity) {
stopCurrentExecutor();
this.mainActivity = mainActivity;
@ -522,26 +526,27 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(mainActivity, Utils.convertDpToPx(110));
mainActivity.mainBinding.profileView.mainPosts.setLayoutManager(layoutManager);
mainActivity.mainBinding.profileView.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
mainActivity.mainBinding.profileView.mainPosts.setAdapter(postsAdapter = new PostsAdapter(mainActivity.allItems, v -> {
final Object tag = v.getTag();
if (tag instanceof PostModel) {
final PostModel postModel = (PostModel) tag;
if (postsAdapter.isSelecting) toggleSelection(postModel);
else mainActivity.startActivity(new Intent(mainActivity, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, postModel.getPosition())
.putExtra(Constants.EXTRAS_POST, postModel)
.putExtra(Constants.EXTRAS_USER, mainActivity.userQuery)
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.MAIN_ITEMS));
}
}, v -> { // long click listener
final Object tag = v.getTag();
if (tag instanceof PostModel) {
postsAdapter.isSelecting = true;
toggleSelection((PostModel) tag);
}
return true;
}));
// mainActivity.mainBinding.profileView.mainPosts.setAdapter(postsAdapter = new PostsAdapter(/*mainActivity.allItems,*/ v -> {
// final Object tag = v.getTag();
// if (tag instanceof PostModel) {
// final PostModel postModel = (PostModel) tag;
//
// if (postsAdapter.isSelecting) toggleSelection(postModel);
// else mainActivity.startActivity(new Intent(mainActivity, PostViewer.class)
// .putExtra(Constants.EXTRAS_INDEX, postModel.getPosition())
// .putExtra(Constants.EXTRAS_POST, postModel)
// .putExtra(Constants.EXTRAS_USER, mainActivity.userQuery)
// .putExtra(Constants.EXTRAS_TYPE, ItemGetType.MAIN_ITEMS));
// }
// }, v -> { // long click listener
// // final Object tag = v.getTag();
// // if (tag instanceof PostModel) {
// // postsAdapter.isSelecting = true;
// // toggleSelection((PostModel) tag);
// // }
// // return true;
// return false;
// }));
this.lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if ((!autoloadPosts || isHashtag) && hasNextPage) {
@ -643,18 +648,18 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
private void setupFeed() {
mainActivity.mainBinding.feedView.feedStories.setLayoutManager(new LinearLayoutManager(mainActivity, LinearLayoutManager.HORIZONTAL, false));
mainActivity.mainBinding.feedView.feedStories.setAdapter(feedStoriesAdapter);
// mainActivity.mainBinding.feedView.feedStories.setAdapter(feedStoriesAdapter);
refreshFeedStories();
final LinearLayoutManager layoutManager = new LinearLayoutManager(mainActivity);
mainActivity.mainBinding.feedView.feedPosts.setHasFixedSize(true);
mainActivity.mainBinding.feedView.feedPosts.setLayoutManager(layoutManager);
mainActivity.mainBinding.feedView.feedPosts.setAdapter(feedAdapter = new FeedAdapter(glide, clickListener, (view, text, isHashtag) ->
mainActivity.mainBinding.feedView.feedPosts.setAdapter(feedAdapter = new FeedAdapter(clickListener, (view, text, isHashtag) ->
new AlertDialog.Builder(mainActivity).setMessage(isHashtag ? R.string.comment_view_mention_hash_search : R.string.comment_view_mention_user_search)
.setTitle(text).setNegativeButton(R.string.cancel, null).setPositiveButton(R.string.ok, (dialog, which) -> {
if (MainActivity.scanHack != null) {
if (MainActivityBackup.scanHack != null) {
mainActivity.mainBinding.drawerLayout.closeDrawers();
MainActivity.scanHack.onResult(text);
MainActivityBackup.scanHack.onResult(text);
}
}).show()));
@ -727,25 +732,25 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
new DiscoverFetcher(topic, null, rankToken, discoverFetchListener, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
mainActivity.mainBinding.discoverPosts.setAdapter(discoverAdapter = new DiscoverAdapter(mainActivity.discoverItems, v -> {
final Object tag = v.getTag();
if (tag instanceof DiscoverItemModel) {
final DiscoverItemModel itemModel = (DiscoverItemModel) tag;
if (discoverAdapter.isSelecting) toggleDiscoverSelection(itemModel);
else mainActivity.startActivity(new Intent(mainActivity, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, itemModel.getPosition())
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.DISCOVER_ITEMS)
.putExtra(Constants.EXTRAS_POST, new PostModel(itemModel.getShortCode(), false)));
}
}, v -> {
final Object tag = v.getTag();
if (tag instanceof DiscoverItemModel) {
discoverAdapter.isSelecting = true;
toggleDiscoverSelection((DiscoverItemModel) tag);
}
return true;
}));
// mainActivity.mainBinding.discoverPosts.setAdapter(discoverAdapter = new DiscoverAdapter(mainActivity.discoverItems, v -> {
// final Object tag = v.getTag();
// if (tag instanceof DiscoverItemModel) {
// final DiscoverItemModel itemModel = (DiscoverItemModel) tag;
//
// if (discoverAdapter.isSelecting) toggleDiscoverSelection(itemModel);
// else mainActivity.startActivity(new Intent(mainActivity, PostViewer.class)
// .putExtra(Constants.EXTRAS_INDEX, itemModel.getPosition())
// .putExtra(Constants.EXTRAS_TYPE, ItemGetType.DISCOVER_ITEMS)
// .putExtra(Constants.EXTRAS_POST, new PostModel(itemModel.getShortCode(), false)));
// }
// }, v -> {
// final Object tag = v.getTag();
// if (tag instanceof DiscoverItemModel) {
// discoverAdapter.isSelecting = true;
// toggleDiscoverSelection((DiscoverItemModel) tag);
// }
// return true;
// }));
mainActivity.mainBinding.discoverPosts.addOnScrollListener(discoverLazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if (discoverHasMore) {
@ -811,7 +816,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
mainActivity.allItems.clear();
mainActivity.selectedItems.clear();
if (postsAdapter != null) {
postsAdapter.isSelecting = false;
// postsAdapter.isSelecting = false;
postsAdapter.notifyDataSetChanged();
}
mainActivity.mainBinding.profileView.appBarLayout.setExpanded(true, true);
@ -1279,12 +1284,12 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
}
private void notifyAdapter(final PostModel postModel) {
if (mainActivity.selectedItems.size() < 1) postsAdapter.isSelecting = false;
if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged();
else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel);
if (mainActivity.downloadAction != null)
mainActivity.downloadAction.setVisible(postsAdapter.isSelecting);
// if (mainActivity.selectedItems.size() < 1) postsAdapter.isSelecting = false;
// if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged();
// else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel);
//
// if (mainActivity.downloadAction != null)
// mainActivity.downloadAction.setVisible(postsAdapter.isSelecting);
}
private void toggleDiscoverSelection(final DiscoverItemModel itemModel) {
@ -1297,33 +1302,34 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
}
private void notifyDiscoverAdapter(final DiscoverItemModel itemModel) {
if (mainActivity.selectedDiscoverItems.size() < 1) discoverAdapter.isSelecting = false;
if (itemModel.getPosition() < 0) discoverAdapter.notifyDataSetChanged();
else discoverAdapter.notifyItemChanged(itemModel.getPosition(), itemModel);
if (mainActivity.downloadAction != null)
mainActivity.downloadAction.setVisible(discoverAdapter.isSelecting);
// if (mainActivity.selectedDiscoverItems.size() < 1) discoverAdapter.isSelecting = false;
// if (itemModel.getPosition() < 0) discoverAdapter.notifyDataSetChanged();
// else discoverAdapter.notifyItemChanged(itemModel.getPosition(), itemModel);
//
// if (mainActivity.downloadAction != null)
// mainActivity.downloadAction.setVisible(discoverAdapter.isSelecting);
}
public boolean isSelectionCleared() {
if (postsAdapter != null && postsAdapter.isSelecting) {
for (final PostModel postModel : mainActivity.selectedItems)
postModel.setSelected(false);
mainActivity.selectedItems.clear();
postsAdapter.isSelecting = false;
postsAdapter.notifyDataSetChanged();
if (mainActivity.downloadAction != null) mainActivity.downloadAction.setVisible(false);
// if (postsAdapter != null && postsAdapter.isSelecting()) {
// for (final PostModel postModel : mainActivity.selectedItems)
// postModel.setSelected(false);
// mainActivity.selectedItems.clear();
// // postsAdapter.isSelecting = false;
// postsAdapter.notifyDataSetChanged();
// if (mainActivity.downloadAction != null) mainActivity.downloadAction.setVisible(false);
// return false;
// } else if (discoverAdapter != null && discoverAdapter.isSelecting) {
// for (final DiscoverItemModel itemModel : mainActivity.selectedDiscoverItems)
// itemModel.setSelected(false);
// mainActivity.selectedDiscoverItems.clear();
// discoverAdapter.isSelecting = false;
// discoverAdapter.notifyDataSetChanged();
// if (mainActivity.downloadAction != null) mainActivity.downloadAction.setVisible(false);
// return false;
// }
// return true;
return false;
} else if (discoverAdapter != null && discoverAdapter.isSelecting) {
for (final DiscoverItemModel itemModel : mainActivity.selectedDiscoverItems)
itemModel.setSelected(false);
mainActivity.selectedDiscoverItems.clear();
discoverAdapter.isSelecting = false;
discoverAdapter.notifyDataSetChanged();
if (mainActivity.downloadAction != null) mainActivity.downloadAction.setVisible(false);
return false;
}
return true;
}
public void deselectSelection(final BasePostModel postModel) {
@ -1364,7 +1370,8 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
private final View.OnClickListener profileActionListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
final boolean iamme = (isLoggedIn && mainActivity.profileModel != null) && Utils.getUserIdFromCookie(cookie).equals(mainActivity.profileModel.getId());
final String userIdFromCookie = Utils.getUserIdFromCookie(MainHelper.this.cookie);
final boolean isSelf = (isLoggedIn && mainActivity.profileModel != null) && userIdFromCookie != null && userIdFromCookie.equals(mainActivity.profileModel.getId());
if (!isLoggedIn && Utils.dataBox.getFavorite(mainActivity.userQuery) != null && v == mainActivity.mainBinding.profileView.btnFollow) {
Utils.dataBox.delFavorite(new DataBox.FavoriteModel(mainActivity.userQuery,
Long.parseLong(Utils.dataBox.getFavorite(mainActivity.userQuery).split("/")[1]),
@ -1378,7 +1385,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
new ProfileAction().execute("follow");
} else if (v == mainActivity.mainBinding.profileView.btnRestrict && isLoggedIn) {
new ProfileAction().execute("restrict");
} else if (v == mainActivity.mainBinding.profileView.btnSaved && !iamme) {
} else if (v == mainActivity.mainBinding.profileView.btnSaved && !isSelf) {
new ProfileAction().execute("block");
} else if (v == mainActivity.mainBinding.profileView.btnFollowTag) {
new ProfileAction().execute("followtag");
@ -1407,17 +1414,12 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
protected Void doInBackground(String... rawAction) {
action = rawAction[0];
final String url = "https://www.instagram.com/web/" +
((action == "followtag" && mainActivity.hashtagModel != null) ? ("tags/" +
(mainActivity.hashtagModel.getFollowing() == true ? "unfollow/" : "follow/") + mainActivity.hashtagModel.getName() + "/") : (
((action == "restrict" && mainActivity.profileModel != null) ? "restrict_action" : ("friendships/" + mainActivity.profileModel.getId())) + "/" +
((action == "follow" && mainActivity.profileModel != null) ?
((mainActivity.profileModel.getFollowing() == true ||
(mainActivity.profileModel.getFollowing() == false && mainActivity.profileModel.getRequested() == true))
? "unfollow/" : "follow/") :
((action == "restrict" && mainActivity.profileModel != null) ?
(mainActivity.profileModel.getRestricted() == true ? "unrestrict/" : "restrict/") :
(mainActivity.profileModel.getBlocked() == true ? "unblock/" : "block/")))));
final String url = "https://www.instagram.com/web/" + (action.equals("followtag") && mainActivity.hashtagModel != null ? "tags/" + (mainActivity.hashtagModel.getFollowing() ? "unfollow/" : "follow/") + mainActivity.hashtagModel.getName() + "/" : (action.equals("restrict") && mainActivity.profileModel != null ? "restrict_action" : "friendships/" + mainActivity.profileModel.getId()) + "/" + (action.equals("follow") ?
mainActivity.profileModel.getFollowing() || mainActivity.profileModel.getRequested()
? "unfollow/" : "follow/" :
action.equals("restrict") ?
mainActivity.profileModel.getRestricted() ? "unrestrict/" : "restrict/" :
mainActivity.profileModel.getBlocked() ? "unblock/" : "block/"));
try {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");

View File

@ -1,603 +1,132 @@
package awais.instagrabber.activities;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.MatrixCursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.core.app.NotificationCompat;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.lifecycle.LiveData;
import androidx.navigation.NavController;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.appbar.AppBarLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import awais.instagrabber.MainHelper;
import awais.instagrabber.R;
import awais.instagrabber.adapters.HighlightsAdapter;
import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.GetActivityAsyncTask;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.asyncs.UsernameFetcher;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
import awais.instagrabber.customviews.MouseDrawer;
import awais.instagrabber.customviews.helpers.CustomHideBottomViewOnScrollBehavior;
import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.dialogs.AboutDialog;
import awais.instagrabber.dialogs.QuickAccessDialog;
import awais.instagrabber.dialogs.SettingsDialog;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.ItemGetter;
import awais.instagrabber.models.DiscoverItemModel;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.HashtagModel;
import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.CHANNEL_ID;
import static awais.instagrabber.utils.Utils.notificationManager;
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class MainActivity extends BaseLanguageActivity {
private static final int INITIAL_DELAY_MILLIS = 200;
private static final int DELAY_MILLIS = 60000;
public static FetchListener<String> scanHack;
public static ItemGetter itemGetter;
public class MainActivity extends BaseLanguageActivity {
private static final String TAG = "MainActivity";
public final ArrayList<PostModel> allItems = new ArrayList<>();
public final ArrayList<FeedModel> feedItems = new ArrayList<>();
public final ArrayList<DiscoverItemModel> discoverItems = new ArrayList<>();
public final ArrayList<PostModel> selectedItems = new ArrayList<>();
public final ArrayList<DiscoverItemModel> selectedDiscoverItems = new ArrayList<>();
public final HighlightsAdapter highlightsAdapter = new HighlightsAdapter(null, new View.OnClickListener() {
@Override
public void onClick(final View v) {
final Object tag = v.getTag();
if (tag instanceof HighlightModel) {
final HighlightModel highlightModel = (HighlightModel) tag;
new iStoryStatusFetcher(highlightModel.getId(), null, false, false,
(!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), true, result -> {
if (result != null && result.length > 0)
startActivity(new Intent(MainActivity.this, StoryViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
.putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle())
.putExtra(Constants.EXTRAS_STORIES, result)
);
else
Toast.makeText(MainActivity.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
private SuggestionsAdapter suggestionAdapter;
private MenuItem searchAction;
public ActivityMainBinding mainBinding;
public SearchView searchView;
public MenuItem downloadAction, settingsAction, dmsAction, notifAction;
public StoryModel[] storyModels;
public String userQuery = null, cookie, uid = null;
public MainHelper mainHelper;
public ProfileModel profileModel;
public HashtagModel hashtagModel;
public LocationModel locationModel;
private AutoCompleteTextView searchAutoComplete;
private ArrayAdapter<String> profileDialogAdapter;
private DialogInterface.OnClickListener profileDialogListener;
private Stack<String> queriesStack;
private DataBox.CookieModel cookieModel;
private Runnable runnable;
private Handler handler;
private boolean isLoggedIn;
private ActivityMainBinding binding;
private LiveData<NavController> currentNavControllerLiveData;
@Override
protected void onCreate(@Nullable final Bundle bundle) {
super.onCreate(bundle);
mainBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(mainBinding.getRoot());
FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this);
cookie = settingsHelper.getString(Constants.COOKIE);
uid = Utils.getUserIdFromCookie(cookie);
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
final String cookie = settingsHelper.getString(Constants.COOKIE);
Utils.setupCookies(cookie);
setContentView(binding.getRoot());
MainHelper.stopCurrentExecutor();
mainHelper = new MainHelper(this);
if (bundle == null) {
queriesStack = new Stack<>();
userQuery = null;
} else {
setStack(bundle);
userQuery = bundle.getString("query");
}
isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null;
final Toolbar toolbar = binding.toolbar;
setSupportActionBar(toolbar);
itemGetter = itemGetType -> {
if (itemGetType == ItemGetType.MAIN_ITEMS) return allItems;
if (itemGetType == ItemGetType.DISCOVER_ITEMS) return discoverItems;
if (itemGetType == ItemGetType.FEED_ITEMS) return feedItems;
return null;
};
scanHack = result -> {
if (mainHelper != null && !Utils.isEmpty(result)) {
closeAnyOpenDrawer();
addToStack();
userQuery = (result.contains("/") || result.startsWith("#") || result.startsWith("@")) ? result : ("@" + result);
mainHelper.onRefresh();
}
};
// searches for your userid and returns username
if (uid != null) {
final FetchListener<String> fetchListener = username -> {
if (!Utils.isEmpty(username)) {
// if (!BuildConfig.DEBUG) {
userQuery = username;
if (mainHelper != null && !mainBinding.profileView.swipeRefreshLayout.isRefreshing())
mainHelper.onRefresh();
// }
// adds cookies to database for quick access
cookieModel = Utils.dataBox.getCookie(uid);
if (Utils.dataBox.getCookieCount() == 0 || cookieModel == null || Utils.isEmpty(cookieModel.getUsername()))
Utils.dataBox.addUserCookie(new DataBox.CookieModel(uid, username, cookie));
}
};
boolean found = false;
cookieModel = Utils.dataBox.getCookie(uid);
if (cookieModel != null) {
final String username = cookieModel.getUsername();
if (username != null) {
found = true;
fetchListener.onResult("@" + username);
}
if (savedInstanceState == null) {
setupBottomNavigationBar();
}
if (!found) // if not in database, fetch info from instagram
new UsernameFetcher(uid, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
setupScrollingListener();
}
suggestionAdapter = new SuggestionsAdapter(this, v -> {
final Object tag = v.getTag();
if (tag instanceof CharSequence) {
addToStack();
userQuery = tag.toString();
mainHelper.onRefresh();
private void setupScrollingListener() {
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.bottomNavView.getLayoutParams();
layoutParams.setBehavior(new CustomHideBottomViewOnScrollBehavior());
binding.bottomNavView.requestLayout();
}
if (searchView != null && !searchView.isIconified()) {
if (searchAction != null) searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
});
final Resources resources = getResources();
profileDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
new String[]{resources.getString(R.string.view_pfp), resources.getString(R.string.show_stories)});
profileDialogListener = (dialog, which) -> {
final Intent intent;
if (which == 0 || storyModels == null || storyModels.length < 1) {
intent = new Intent(this, ProfilePicViewer.class).putExtra(
((hashtagModel != null) ? Constants.EXTRAS_HASHTAG : (locationModel != null ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_PROFILE)),
((hashtagModel != null) ? hashtagModel : (locationModel != null ? locationModel : profileModel)));
} else
intent = new Intent(this, StoryViewer.class).putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
.putExtra(Constants.EXTRAS_STORIES, storyModels)
.putExtra(Constants.EXTRAS_HASHTAG, (hashtagModel != null));
startActivity(intent);
};
private void setupBottomNavigationBar() {
final List<Integer> navList = new ArrayList<>(Arrays.asList(
R.navigation.direct_messages_nav_graph,
R.navigation.feed_nav_graph,
R.navigation.profile_nav_graph,
R.navigation.discover_nav_graph
));
final View.OnClickListener onClickListener = v -> {
if (v == mainBinding.profileView.mainBiography) {
Utils.copyText(this, mainBinding.profileView.mainBiography.getText().toString());
} else if (v == mainBinding.profileView.locationBiography) {
Utils.copyText(this, mainBinding.profileView.locationBiography.getText().toString());
} else if (v == mainBinding.profileView.mainProfileImage || v == mainBinding.profileView.mainHashtagImage || v == mainBinding.profileView.mainLocationImage) {
if (storyModels == null || storyModels.length <= 0) {
profileDialogListener.onClick(null, 0);
} else {
// because sometimes configuration changes made this crash on some phones
new AlertDialog.Builder(this).setAdapter(profileDialogAdapter, profileDialogListener)
.setNeutralButton(R.string.cancel, null).show();
binding.bottomNavView.setSelectedItemId(R.id.feed_nav_graph);
final LiveData<NavController> navControllerLiveData = setupWithNavController(
binding.bottomNavView,
navList,
getSupportFragmentManager(),
R.id.main_nav_host,
getIntent(),
1);
navControllerLiveData.observe(this, this::setupNavigation);
currentNavControllerLiveData = navControllerLiveData;
}
}
};
mainBinding.profileView.mainBiography.setOnClickListener(onClickListener);
mainBinding.profileView.locationBiography.setOnClickListener(onClickListener);
mainBinding.profileView.mainProfileImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainHashtagImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainLocationImage.setOnClickListener(onClickListener);
private void setupNavigation(final NavController navController) {
NavigationUI.setupWithNavController(binding.toolbar, navController);
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
binding.appBarLayout.setExpanded(true, true);
final int destinationId = destination.getId();
final List<Integer> showBottomView = Arrays.asList(
R.id.directMessagesInboxFragment,
R.id.feedFragment,
R.id.profileFragment,
R.id.discoverFragment);
mainBinding.profileView.mainBiography.setEnabled(false);
mainBinding.profileView.mainProfileImage.setEnabled(false);
mainBinding.profileView.mainHashtagImage.setEnabled(false);
mainBinding.profileView.mainLocationImage.setEnabled(false);
final boolean isQueryNull = userQuery == null;
if (isQueryNull) {
allItems.clear();
mainBinding.profileView.privatePage1.setImageResource(R.drawable.ic_info);
mainBinding.profileView.privatePage2.setTextSize(20);
mainBinding.profileView.privatePage2.setText(isLoggedIn ? R.string.no_acc_logged_in : R.string.no_acc);
mainBinding.profileView.privatePage.setVisibility(View.VISIBLE);
}
if (!mainBinding.profileView.swipeRefreshLayout.isRefreshing() && userQuery != null)
mainHelper.onRefresh();
mainHelper.onIntent(getIntent());
handler = new Handler();
runnable = () -> {
final GetActivityAsyncTask activityAsyncTask = new GetActivityAsyncTask(uid, cookie, result -> {
if (result == null || notificationManager == null) {
if (showBottomView.contains(destinationId)) {
setScrollingBehaviour();
binding.bottomNavView.setVisibility(View.VISIBLE);
return;
}
final List<String> list = new ArrayList<>();
if (result.getRelationshipsCount() != 0) {
list.add(getString(R.string.activity_count_relationship, result.getRelationshipsCount()));
}
if (result.getUserTagsCount() != 0) {
list.add(getString(R.string.activity_count_usertags, result.getUserTagsCount()));
}
if (result.getCommentsCount() != 0) {
list.add(getString(R.string.activity_count_comments, result.getCommentsCount()));
}
if (result.getCommentLikesCount() != 0) {
list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikesCount()));
}
if (result.getLikesCount() != 0) {
list.add(getString(R.string.activity_count_likes, result.getLikesCount()));
}
if (list.isEmpty()) {
return;
}
final String join = TextUtils.join(", ", list);
final String notificationString = getString(R.string.activity_count_prefix) + " " + join + ".";
final Intent intent = new Intent(getApplicationContext(), NotificationsViewer.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Notification notification = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setSmallIcon(R.drawable.ic_notif)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setContentText(notificationString)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 1738, intent, PendingIntent.FLAG_UPDATE_CURRENT))
.build();
notificationManager.cancel(1800000000);
notificationManager.notify(1800000000, notification);
removeScrollingBehaviour();
binding.bottomNavView.setVisibility(View.GONE);
});
activityAsyncTask.execute();
if (!Utils.isEmpty(cookie) && Utils.settingsHelper.getBoolean(Constants.CHECK_ACTIVITY))
activityAsyncTask.execute();
handler.postDelayed(runnable, DELAY_MILLIS);
};
handler.postDelayed(runnable, INITIAL_DELAY_MILLIS);
}
private void downloadSelectedItems() {
if (selectedItems.size() > 0) {
Utils.batchDownload(this, userQuery, DownloadMethod.DOWNLOAD_MAIN, selectedItems);
} else if (selectedDiscoverItems.size() > 0) {
Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_DISCOVER, selectedDiscoverItems);
}
private void setScrollingBehaviour() {
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.mainNavHost.getLayoutParams();
layoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
binding.mainNavHost.requestLayout();
}
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
mainHelper.onIntent(intent);
}
@Override
public void onSaveInstanceState(@NonNull final Bundle outState, @NonNull final PersistableBundle outPersistentState) {
outState.putString("query", userQuery);
outState.putSerializable("stack", queriesStack);
super.onSaveInstanceState(outState, outPersistentState);
}
@Override
public void onRestoreInstanceState(@Nullable final Bundle savedInstanceState, @Nullable final PersistableBundle persistentState) {
super.onRestoreInstanceState(savedInstanceState, persistentState);
if (savedInstanceState != null) {
userQuery = savedInstanceState.getString("query");
setStack(savedInstanceState);
}
}
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString("query", userQuery);
outState.putSerializable("stack", queriesStack);
super.onSaveInstanceState(outState);
private void removeScrollingBehaviour() {
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.mainNavHost.getLayoutParams();
layoutParams.setBehavior(null);
binding.mainNavHost.requestLayout();
}
@Override
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
userQuery = savedInstanceState.getString("query");
setStack(savedInstanceState);
setupBottomNavigationBar();
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
final FragmentManager fragmentManager = getSupportFragmentManager();
final MenuItem quickAccessAction = menu.findItem(R.id.action_quickaccess).setVisible(true);
final MenuItem.OnMenuItemClickListener clickListener = item -> {
if (item == downloadAction)
downloadSelectedItems();
else if (item == dmsAction)
startActivity(new Intent(this, DirectMessagesActivity.class));
else if (item == notifAction)
startActivity(new Intent(this, NotificationsViewer.class));
else if (item == settingsAction)
new SettingsDialog().show(fragmentManager, "settings");
else if (item == quickAccessAction)
new QuickAccessDialog()
.setQuery(userQuery, locationModel != null ? locationModel.getName() : userQuery)
.show(fragmentManager, "quickAccess");
else
new AboutDialog().show(fragmentManager, "about");
return true;
};
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))) {
notifAction.setVisible(true);
dmsAction.setVisible(true).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
searchAction = menu.findItem(R.id.action_search);
searchView = (SearchView) searchAction.getActionView();
final View searchText = searchView.findViewById(R.id.search_src_text);
if (searchText instanceof AutoCompleteTextView)
searchAutoComplete = (AutoCompleteTextView) searchText;
searchView.setQueryHint(getResources().getString(R.string.action_search));
searchView.setSuggestionsAdapter(suggestionAdapter);
searchView.setOnSearchClickListener(v -> {
searchView.setQuery((cookieModel != null && userQuery != null && userQuery.equals("@" + cookieModel.getUsername())) ? "" : userQuery, false);
menu.findItem(R.id.action_about).setVisible(false);
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
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
menu.findItem(R.id.action_about).setVisible(true);
menu.findItem(R.id.action_settings).setVisible(true);
menu.findItem(R.id.action_dms).setVisible(!Utils.isEmpty(Utils.settingsHelper.getString(Constants.COOKIE)));
menu.findItem(R.id.action_quickaccess).setVisible(true);
menu.findItem(R.id.action_notif).setVisible(true);
return true;
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean searchUser, searchHash;
private AsyncTask<?, ?, ?> prevSuggestionAsync;
private final String[] COLUMNS = {BaseColumns._ID, Constants.EXTRAS_USERNAME, Constants.EXTRAS_NAME,
Constants.EXTRAS_TYPE, "pfp", "verified"};
private final FetchListener<SuggestionModel[]> fetchListener = new FetchListener<SuggestionModel[]>() {
@Override
public void doBefore() {
suggestionAdapter.changeCursor(null);
}
@Override
public void onResult(final SuggestionModel[] result) {
final MatrixCursor cursor;
if (result == null) cursor = null;
else {
cursor = new MatrixCursor(COLUMNS, 0);
for (int i = 0; i < result.length; i++) {
final SuggestionModel suggestionModel = result[i];
if (suggestionModel != null) {
final SuggestionType suggestionType = suggestionModel.getSuggestionType();
final Object[] objects = {i,
(suggestionType == SuggestionType.TYPE_LOCATION) ? suggestionModel.getName() : suggestionModel.getUsername(),
(suggestionType == SuggestionType.TYPE_LOCATION) ? suggestionModel.getUsername() : suggestionModel.getName(),
suggestionType, suggestionModel.getProfilePic(), suggestionModel.isVerified()};
if (!searchHash && !searchUser) cursor.addRow(objects);
else {
final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG;
if (searchHash && isCurrHash || !searchHash && !isCurrHash)
cursor.addRow(objects);
}
}
}
}
suggestionAdapter.changeCursor(cursor);
}
};
private void cancelSuggestionsAsync() {
if (prevSuggestionAsync != null)
try {
prevSuggestionAsync.cancel(true);
} catch (final Exception ignored) {
}
}
@Override
public boolean onQueryTextSubmit(final String query) {
cancelSuggestionsAsync();
menu.findItem(R.id.action_about).setVisible(true);
menu.findItem(R.id.action_settings).setVisible(true);
closeAnyOpenDrawer();
addToStack();
userQuery = (query.contains("@") || query.contains("#")) ? query : ("@" + query);
searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
mainHelper.onRefresh();
return false;
}
@Override
public boolean onQueryTextChange(final String newText) {
cancelSuggestionsAsync();
if (!Utils.isEmpty(newText)) {
searchUser = newText.charAt(0) == '@';
searchHash = newText.charAt(0) == '#';
if (newText.length() == 1 && (searchHash || searchUser)) {
if (searchAutoComplete != null) searchAutoComplete.setThreshold(2);
} else {
if (searchAutoComplete != null) searchAutoComplete.setThreshold(1);
prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
searchUser || searchHash ? newText.substring(1) : newText);
}
}
return true;
}
});
return true;
}
@Override
public void onBackPressed() {
if (closeAnyOpenDrawer()) return;
if (searchView != null && !searchView.isIconified()) {
if (searchAction != null) searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
return;
}
if (!mainHelper.isSelectionCleared()) return;
final GridLayoutManager layoutManager = (GridLayoutManager) mainBinding.profileView.mainPosts.getLayoutManager();
if (layoutManager != null && layoutManager.findFirstCompletelyVisibleItemPosition() >= layoutManager.getSpanCount()) {
mainBinding.profileView.mainPosts.smoothScrollToPosition(0);
mainBinding.profileView.appBarLayout.setExpanded(true, true);
return;
}
if (queriesStack != null && queriesStack.size() > 0) {
userQuery = queriesStack.pop();
if (userQuery != null) {
mainHelper.onRefresh();
return;
}
} else {
finish();
}
}
@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)
downloadSelectedItems();
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 9629 && (resultCode == 1692 || resultCode == RESULT_CANCELED))
finish();
else if (requestCode == 6007)
Utils.showImportExportDialog(this);
// else if (requestCode == 6969 && mainHelper.currentFeedPlayer != null)
// mainHelper.currentFeedPlayer.setPlayWhenReady(true);
}
@Override
protected void onPause() {
if (mainHelper != null) mainHelper.onPause();
if (handler != null && runnable != null) {
handler.removeCallbacks(runnable);
}
super.onPause();
}
@Override
protected void onResume() {
if (mainHelper != null) mainHelper.onResume();
if (handler != null && runnable != null) {
handler.postDelayed(runnable, INITIAL_DELAY_MILLIS);
}
super.onResume();
}
private void setStack(final Bundle bundle) {
final Object stack = bundle != null ? bundle.get("stack") : null;
if (stack instanceof Stack) //noinspection unchecked
queriesStack = (Stack<String>) stack;
}
public void addToStack() {
if (userQuery != null) {
if (queriesStack == null) queriesStack = new Stack<>();
queriesStack.add(userQuery);
}
}
private boolean closeAnyOpenDrawer() {
final int childCount = mainBinding.drawerLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = mainBinding.drawerLayout.getChildAt(i);
final MouseDrawer.LayoutParams childLp = (MouseDrawer.LayoutParams) child.getLayoutParams();
if ((childLp.openState & MouseDrawer.LayoutParams.FLAG_IS_OPENED) == 1 ||
(childLp.openState & MouseDrawer.LayoutParams.FLAG_IS_OPENING) == 2 ||
childLp.onScreen >= 0.6 || childLp.isPeeking) {
mainBinding.drawerLayout.closeDrawer(child);
return true;
}
public boolean onSupportNavigateUp() {
if (currentNavControllerLiveData != null && currentNavControllerLiveData.getValue() != null) {
return currentNavControllerLiveData.getValue().navigateUp();
}
return false;
}

View File

@ -0,0 +1,607 @@
package awais.instagrabber.activities;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.MatrixCursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.core.app.NotificationCompat;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.GridLayoutManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import awais.instagrabber.MainHelper;
import awais.instagrabber.R;
import awais.instagrabber.adapters.HighlightsAdapter;
import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.GetActivityAsyncTask;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.asyncs.UsernameFetcher;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
import awais.instagrabber.customviews.MouseDrawer;
import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.databinding.ActivityMainbackupBinding;
import awais.instagrabber.dialogs.AboutDialog;
import awais.instagrabber.dialogs.QuickAccessDialog;
import awais.instagrabber.dialogs.SettingsDialog;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.ItemGetter;
import awais.instagrabber.models.DiscoverItemModel;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.HashtagModel;
import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.CHANNEL_ID;
import static awais.instagrabber.utils.Utils.notificationManager;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class MainActivityBackup extends BaseLanguageActivity {
private static final int INITIAL_DELAY_MILLIS = 200;
private static final int DELAY_MILLIS = 60000;
public static FetchListener<String> scanHack;
public static ItemGetter itemGetter;
public final ArrayList<PostModel> allItems = new ArrayList<>();
public final ArrayList<FeedModel> feedItems = new ArrayList<>();
public final ArrayList<DiscoverItemModel> discoverItems = new ArrayList<>();
public final ArrayList<PostModel> selectedItems = new ArrayList<>();
public final ArrayList<DiscoverItemModel> selectedDiscoverItems = new ArrayList<>();
public final HighlightsAdapter highlightsAdapter = new HighlightsAdapter(null, new View.OnClickListener() {
@Override
public void onClick(final View v) {
final Object tag = v.getTag();
if (tag instanceof HighlightModel) {
final HighlightModel highlightModel = (HighlightModel) tag;
new iStoryStatusFetcher(highlightModel.getId(), null, false, false,
(!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), true, result -> {
if (result != null && result.length > 0) {
// startActivity(new Intent(MainActivityBackup.this, StoryViewer.class)
// .putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
// .putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle())
// .putExtra(Constants.EXTRAS_STORIES, result)
// );
}
else
Toast.makeText(MainActivityBackup.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
private SuggestionsAdapter suggestionAdapter;
private MenuItem searchAction;
public @NonNull ActivityMainbackupBinding mainBinding;
public SearchView searchView;
public MenuItem downloadAction, settingsAction, dmsAction, notifAction;
public StoryModel[] storyModels;
public String userQuery = null, cookie, uid = null;
public MainHelper mainHelper;
public ProfileModel profileModel;
public HashtagModel hashtagModel;
public LocationModel locationModel;
private AutoCompleteTextView searchAutoComplete;
private ArrayAdapter<String> profileDialogAdapter;
private DialogInterface.OnClickListener profileDialogListener;
private Stack<String> queriesStack;
private DataBox.CookieModel cookieModel;
private Runnable runnable;
private Handler handler;
private boolean isLoggedIn;
@Override
protected void onCreate(@Nullable final Bundle bundle) {
super.onCreate(bundle);
mainBinding = ActivityMainbackupBinding.inflate(getLayoutInflater());
setContentView(mainBinding.getRoot());
FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this);
cookie = settingsHelper.getString(Constants.COOKIE);
uid = Utils.getUserIdFromCookie(cookie);
Utils.setupCookies(cookie);
MainHelper.stopCurrentExecutor();
mainHelper = new MainHelper(this);
if (bundle == null) {
queriesStack = new Stack<>();
userQuery = null;
} else {
setStack(bundle);
userQuery = bundle.getString("query");
}
isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null;
itemGetter = itemGetType -> {
if (itemGetType == ItemGetType.MAIN_ITEMS) return allItems;
if (itemGetType == ItemGetType.DISCOVER_ITEMS) return discoverItems;
if (itemGetType == ItemGetType.FEED_ITEMS) return feedItems;
return null;
};
scanHack = result -> {
if (mainHelper != null && !Utils.isEmpty(result)) {
closeAnyOpenDrawer();
addToStack();
userQuery = (result.contains("/") || result.startsWith("#") || result.startsWith("@")) ? result : ("@" + result);
mainHelper.onRefresh();
}
};
// searches for your userid and returns username
if (uid != null) {
final FetchListener<String> fetchListener = username -> {
if (!Utils.isEmpty(username)) {
// if (!BuildConfig.DEBUG) {
userQuery = username;
if (mainHelper != null && !mainBinding.profileView.swipeRefreshLayout.isRefreshing())
mainHelper.onRefresh();
// }
// adds cookies to database for quick access
cookieModel = Utils.dataBox.getCookie(uid);
if (Utils.dataBox.getCookieCount() == 0 || cookieModel == null || Utils.isEmpty(cookieModel.getUsername()))
Utils.dataBox.addUserCookie(new DataBox.CookieModel(uid, username, cookie));
}
};
boolean found = false;
cookieModel = Utils.dataBox.getCookie(uid);
if (cookieModel != null) {
final String username = cookieModel.getUsername();
if (username != null) {
found = true;
fetchListener.onResult("@" + username);
}
}
if (!found) // if not in database, fetch info from instagram
new UsernameFetcher(uid, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
suggestionAdapter = new SuggestionsAdapter(this, v -> {
final Object tag = v.getTag();
if (tag instanceof CharSequence) {
addToStack();
userQuery = tag.toString();
mainHelper.onRefresh();
}
if (searchView != null && !searchView.isIconified()) {
if (searchAction != null) searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
}
});
final Resources resources = getResources();
profileDialogAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
new String[]{resources.getString(R.string.view_pfp), resources.getString(R.string.show_stories)});
profileDialogListener = (dialog, which) -> {
final Intent intent;
if (which == 0 || storyModels == null || storyModels.length < 1) {
intent = new Intent(this, ProfilePicViewer.class).putExtra(
((hashtagModel != null) ? Constants.EXTRAS_HASHTAG : (locationModel != null ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_PROFILE)),
((hashtagModel != null) ? hashtagModel : (locationModel != null ? locationModel : profileModel)));
} else {
// intent = new Intent(this, StoryViewer.class).putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
// .putExtra(Constants.EXTRAS_STORIES, storyModels)
// .putExtra(Constants.EXTRAS_HASHTAG, (hashtagModel != null));
}
// startActivity(intent);
};
final View.OnClickListener onClickListener = v -> {
if (v == mainBinding.profileView.mainBiography) {
Utils.copyText(this, mainBinding.profileView.mainBiography.getText().toString());
} else if (v == mainBinding.profileView.locationBiography) {
Utils.copyText(this, mainBinding.profileView.locationBiography.getText().toString());
} else if (v == mainBinding.profileView.mainProfileImage || v == mainBinding.profileView.mainHashtagImage || v == mainBinding.profileView.mainLocationImage) {
if (storyModels == null || storyModels.length <= 0) {
profileDialogListener.onClick(null, 0);
} else {
// because sometimes configuration changes made this crash on some phones
new AlertDialog.Builder(this).setAdapter(profileDialogAdapter, profileDialogListener)
.setNeutralButton(R.string.cancel, null).show();
}
}
};
mainBinding.profileView.mainBiography.setOnClickListener(onClickListener);
mainBinding.profileView.locationBiography.setOnClickListener(onClickListener);
mainBinding.profileView.mainProfileImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainHashtagImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainLocationImage.setOnClickListener(onClickListener);
mainBinding.profileView.mainBiography.setEnabled(false);
mainBinding.profileView.mainProfileImage.setEnabled(false);
mainBinding.profileView.mainHashtagImage.setEnabled(false);
mainBinding.profileView.mainLocationImage.setEnabled(false);
final boolean isQueryNull = userQuery == null;
if (isQueryNull) {
allItems.clear();
mainBinding.profileView.privatePage1.setImageResource(R.drawable.ic_info);
mainBinding.profileView.privatePage2.setTextSize(20);
mainBinding.profileView.privatePage2.setText(isLoggedIn ? R.string.no_acc_logged_in : R.string.no_acc);
mainBinding.profileView.privatePage.setVisibility(View.VISIBLE);
}
if (!mainBinding.profileView.swipeRefreshLayout.isRefreshing() && userQuery != null)
mainHelper.onRefresh();
mainHelper.onIntent(getIntent());
handler = new Handler();
runnable = () -> {
final GetActivityAsyncTask activityAsyncTask = new GetActivityAsyncTask(uid, cookie, result -> {
if (result == null || notificationManager == null) {
return;
}
final List<String> list = new ArrayList<>();
if (result.getRelationshipsCount() != 0) {
list.add(getString(R.string.activity_count_relationship, result.getRelationshipsCount()));
}
if (result.getUserTagsCount() != 0) {
list.add(getString(R.string.activity_count_usertags, result.getUserTagsCount()));
}
if (result.getCommentsCount() != 0) {
list.add(getString(R.string.activity_count_comments, result.getCommentsCount()));
}
if (result.getCommentLikesCount() != 0) {
list.add(getString(R.string.activity_count_commentlikes, result.getCommentLikesCount()));
}
if (result.getLikesCount() != 0) {
list.add(getString(R.string.activity_count_likes, result.getLikesCount()));
}
if (list.isEmpty()) {
return;
}
final String join = TextUtils.join(", ", list);
final String notificationString = getString(R.string.activity_count_prefix) + " " + join + ".";
final Intent intent = new Intent(getApplicationContext(), NotificationsViewer.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Notification notification = new NotificationCompat.Builder(MainActivityBackup.this, CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setSmallIcon(R.drawable.ic_notif)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setContentText(notificationString)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 1738, intent, PendingIntent.FLAG_UPDATE_CURRENT))
.build();
notificationManager.cancel(1800000000);
notificationManager.notify(1800000000, notification);
});
activityAsyncTask.execute();
if (!Utils.isEmpty(cookie) && Utils.settingsHelper.getBoolean(Constants.CHECK_ACTIVITY))
activityAsyncTask.execute();
handler.postDelayed(runnable, DELAY_MILLIS);
};
handler.postDelayed(runnable, INITIAL_DELAY_MILLIS);
}
private void downloadSelectedItems() {
if (selectedItems.size() > 0) {
Utils.batchDownload(this, userQuery, DownloadMethod.DOWNLOAD_MAIN, selectedItems);
} else if (selectedDiscoverItems.size() > 0) {
Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_DISCOVER, selectedDiscoverItems);
}
}
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
mainHelper.onIntent(intent);
}
@Override
public void onSaveInstanceState(@NonNull final Bundle outState, @NonNull final PersistableBundle outPersistentState) {
outState.putString("query", userQuery);
outState.putSerializable("stack", queriesStack);
super.onSaveInstanceState(outState, outPersistentState);
}
@Override
public void onRestoreInstanceState(@Nullable final Bundle savedInstanceState, @Nullable final PersistableBundle persistentState) {
super.onRestoreInstanceState(savedInstanceState, persistentState);
if (savedInstanceState != null) {
userQuery = savedInstanceState.getString("query");
setStack(savedInstanceState);
}
}
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString("query", userQuery);
outState.putSerializable("stack", queriesStack);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
userQuery = savedInstanceState.getString("query");
setStack(savedInstanceState);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
final FragmentManager fragmentManager = getSupportFragmentManager();
final MenuItem quickAccessAction = menu.findItem(R.id.action_quickaccess).setVisible(true);
final MenuItem.OnMenuItemClickListener clickListener = item -> {
if (item == downloadAction)
downloadSelectedItems();
else if (item == dmsAction)
startActivity(new Intent(this, DirectMessagesActivity.class));
else if (item == notifAction)
startActivity(new Intent(this, NotificationsViewer.class));
else if (item == settingsAction)
new SettingsDialog().show(fragmentManager, "settings");
else if (item == quickAccessAction)
new QuickAccessDialog()
.setQuery(userQuery, locationModel != null ? locationModel.getName() : userQuery)
.show(fragmentManager, "quickAccess");
else
new AboutDialog().show(fragmentManager, "about");
return true;
};
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))) {
notifAction.setVisible(true);
dmsAction.setVisible(true).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
searchAction = menu.findItem(R.id.action_search);
searchView = (SearchView) searchAction.getActionView();
final View searchText = searchView.findViewById(R.id.search_src_text);
if (searchText instanceof AutoCompleteTextView)
searchAutoComplete = (AutoCompleteTextView) searchText;
searchView.setQueryHint(getResources().getString(R.string.action_search));
searchView.setSuggestionsAdapter(suggestionAdapter);
searchView.setOnSearchClickListener(v -> {
searchView.setQuery((cookieModel != null && userQuery != null && userQuery.equals("@" + cookieModel.getUsername())) ? "" : userQuery, false);
menu.findItem(R.id.action_about).setVisible(false);
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
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
menu.findItem(R.id.action_about).setVisible(true);
menu.findItem(R.id.action_settings).setVisible(true);
menu.findItem(R.id.action_dms).setVisible(!Utils.isEmpty(Utils.settingsHelper.getString(Constants.COOKIE)));
menu.findItem(R.id.action_quickaccess).setVisible(true);
menu.findItem(R.id.action_notif).setVisible(true);
return true;
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean searchUser, searchHash;
private AsyncTask<?, ?, ?> prevSuggestionAsync;
private final String[] COLUMNS = {BaseColumns._ID, Constants.EXTRAS_USERNAME, Constants.EXTRAS_NAME,
Constants.EXTRAS_TYPE, "pfp", "verified"};
private final FetchListener<SuggestionModel[]> fetchListener = new FetchListener<SuggestionModel[]>() {
@Override
public void doBefore() {
suggestionAdapter.changeCursor(null);
}
@Override
public void onResult(final SuggestionModel[] result) {
final MatrixCursor cursor;
if (result == null) cursor = null;
else {
cursor = new MatrixCursor(COLUMNS, 0);
for (int i = 0; i < result.length; i++) {
final SuggestionModel suggestionModel = result[i];
if (suggestionModel != null) {
final SuggestionType suggestionType = suggestionModel.getSuggestionType();
final Object[] objects = {i,
(suggestionType == SuggestionType.TYPE_LOCATION) ? suggestionModel.getName() : suggestionModel.getUsername(),
(suggestionType == SuggestionType.TYPE_LOCATION) ? suggestionModel.getUsername() : suggestionModel.getName(),
suggestionType, suggestionModel.getProfilePic(), suggestionModel.isVerified()};
if (!searchHash && !searchUser) cursor.addRow(objects);
else {
final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG;
if (searchHash && isCurrHash || !searchHash && !isCurrHash)
cursor.addRow(objects);
}
}
}
}
suggestionAdapter.changeCursor(cursor);
}
};
private void cancelSuggestionsAsync() {
if (prevSuggestionAsync != null)
try {
prevSuggestionAsync.cancel(true);
} catch (final Exception ignored) {
}
}
@Override
public boolean onQueryTextSubmit(final String query) {
cancelSuggestionsAsync();
menu.findItem(R.id.action_about).setVisible(true);
menu.findItem(R.id.action_settings).setVisible(true);
closeAnyOpenDrawer();
addToStack();
userQuery = (query.contains("@") || query.contains("#")) ? query : ("@" + query);
searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
mainHelper.onRefresh();
return false;
}
@Override
public boolean onQueryTextChange(final String newText) {
cancelSuggestionsAsync();
if (!Utils.isEmpty(newText)) {
searchUser = newText.charAt(0) == '@';
searchHash = newText.charAt(0) == '#';
if (newText.length() == 1 && (searchHash || searchUser)) {
if (searchAutoComplete != null) searchAutoComplete.setThreshold(2);
} else {
if (searchAutoComplete != null) searchAutoComplete.setThreshold(1);
prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
searchUser || searchHash ? newText.substring(1) : newText);
}
}
return true;
}
});
return true;
}
@Override
public void onBackPressed() {
if (closeAnyOpenDrawer()) return;
if (searchView != null && !searchView.isIconified()) {
if (searchAction != null) searchAction.collapseActionView();
searchView.setIconified(true);
searchView.setIconified(true);
return;
}
if (!mainHelper.isSelectionCleared()) return;
final GridLayoutManager layoutManager = (GridLayoutManager) mainBinding.profileView.mainPosts.getLayoutManager();
if (layoutManager != null && layoutManager.findFirstCompletelyVisibleItemPosition() >= layoutManager.getSpanCount()) {
mainBinding.profileView.mainPosts.smoothScrollToPosition(0);
mainBinding.profileView.appBarLayout.setExpanded(true, true);
return;
}
if (queriesStack != null && queriesStack.size() > 0) {
userQuery = queriesStack.pop();
if (userQuery != null) {
mainHelper.onRefresh();
return;
}
} else {
finish();
}
}
@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)
downloadSelectedItems();
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 9629 && (resultCode == 1692 || resultCode == RESULT_CANCELED))
finish();
else if (requestCode == 6007)
Utils.showImportExportDialog(this);
// else if (requestCode == 6969 && mainHelper.currentFeedPlayer != null)
// mainHelper.currentFeedPlayer.setPlayWhenReady(true);
}
@Override
protected void onPause() {
if (mainHelper != null) mainHelper.onPause();
if (handler != null && runnable != null) {
handler.removeCallbacks(runnable);
}
super.onPause();
}
@Override
protected void onResume() {
if (mainHelper != null) mainHelper.onResume();
if (handler != null && runnable != null) {
handler.postDelayed(runnable, INITIAL_DELAY_MILLIS);
}
super.onResume();
}
private void setStack(final Bundle bundle) {
final Object stack = bundle != null ? bundle.get("stack") : null;
if (stack instanceof Stack) //noinspection unchecked
queriesStack = (Stack<String>) stack;
}
public void addToStack() {
if (userQuery != null) {
if (queriesStack == null) queriesStack = new Stack<>();
queriesStack.add(userQuery);
}
}
private boolean closeAnyOpenDrawer() {
final int childCount = mainBinding.drawerLayout.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = mainBinding.drawerLayout.getChildAt(i);
final MouseDrawer.LayoutParams childLp = (MouseDrawer.LayoutParams) child.getLayoutParams();
if ((childLp.openState & MouseDrawer.LayoutParams.FLAG_IS_OPENED) == 1 ||
(childLp.openState & MouseDrawer.LayoutParams.FLAG_IS_OPENING) == 2 ||
childLp.onScreen >= 0.6 || childLp.isPeeking) {
mainBinding.drawerLayout.closeDrawer(child);
return true;
}
}
return false;
}
}

View File

@ -161,7 +161,7 @@ public final class PostViewer extends BaseLanguageActivity {
if (player != null) {
final float intVol = player.getVolume() == 0f ? 1f : 0f;
player.setVolume(intVol);
viewerBinding.bottomPanel.btnMute.setImageResource(intVol == 0f ? R.drawable.mute : R.drawable.vol);
viewerBinding.bottomPanel.btnMute.setImageResource(intVol == 0f ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
Utils.sessionVolumeFull = intVol == 1f;
}
} else if (v == viewerBinding.btnLike) {
@ -307,7 +307,7 @@ public final class PostViewer extends BaseLanguageActivity {
isFromShare = postModel.getPosition() == -1 || postIdNull;
viewerCaptionParent = (View) viewerBinding.bottomPanel.viewerCaption.getParent();
viewsContainer = (View) viewerBinding.bottomPanel.tvVideoViews.getParent();
viewsContainer = (View) viewerBinding.bottomPanel.videoViewsContainer;
viewerBinding.mediaList.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
viewerBinding.mediaList.setAdapter(mediaAdapter);
@ -320,8 +320,8 @@ public final class PostViewer extends BaseLanguageActivity {
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 && MainActivity.itemGetter != null) {
itemGetterItems = MainActivity.itemGetter.get(itemGetType);
} else if (itemGetType != null && MainActivityBackup.itemGetter != null) {
itemGetterItems = MainActivityBackup.itemGetter.get(itemGetType);
isMainSwipe = !(itemGetterItems.size() < 1 || itemGetType == ItemGetType.MAIN_ITEMS && isFromShare);
} else {
itemGetterItems = null;
@ -449,7 +449,7 @@ public final class PostViewer extends BaseLanguageActivity {
player.prepare(mediaSource);
player.setVolume(vol);
viewerBinding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);
viewerBinding.bottomPanel.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
viewerBinding.bottomPanel.btnMute.setOnClickListener(onClickListener);
}

View File

@ -62,7 +62,6 @@ import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.Utils;
@ -140,13 +139,13 @@ public final class ProfileViewer extends BaseLanguageActivity implements SwipeRe
final HighlightModel highlightModel = (HighlightModel) tag;
new iStoryStatusFetcher(highlightModel.getId(), null, false, false,
(!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), true, result -> {
if (result != null && result.length > 0)
startActivity(new Intent(ProfileViewer.this, StoryViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
.putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle())
.putExtra(Constants.EXTRAS_STORIES, result)
);
else
if (result != null && result.length > 0) {
// startActivity(new Intent(ProfileViewer.this, StoryViewer.class)
// .putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
// .putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle())
// .putExtra(Constants.EXTRAS_STORIES, result)
// );
} else
Toast.makeText(ProfileViewer.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@ -188,11 +187,12 @@ public final class ProfileViewer extends BaseLanguageActivity implements SwipeRe
newintent = new Intent(this, ProfilePicViewer.class).putExtra(
((hashtagModel != null) ? Constants.EXTRAS_HASHTAG : (locationModel != null ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_PROFILE)),
((hashtagModel != null) ? hashtagModel : (locationModel != null ? locationModel : profileModel)));
} else
newintent = new Intent(this, StoryViewer.class).putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
.putExtra(Constants.EXTRAS_STORIES, storyModels)
.putExtra(Constants.EXTRAS_HASHTAG, (hashtagModel != null));
startActivity(newintent);
}
// else
// newintent = new Intent(this, StoryViewer.class).putExtra(Constants.EXTRAS_USERNAME, userQuery.replace("@", ""))
// .putExtra(Constants.EXTRAS_STORIES, storyModels)
// .putExtra(Constants.EXTRAS_HASHTAG, (hashtagModel != null));
// startActivity(newintent);
};
profileBinding.profileView.swipeRefreshLayout.setOnRefreshListener(this);
@ -213,26 +213,26 @@ public final class ProfileViewer extends BaseLanguageActivity implements SwipeRe
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(ProfileViewer.this, Utils.convertDpToPx(110));
profileBinding.profileView.mainPosts.setLayoutManager(layoutManager);
profileBinding.profileView.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
profileBinding.profileView.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(ProfileViewer.this, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, postModel.getPosition())
.putExtra(Constants.EXTRAS_POST, postModel)
.putExtra(Constants.EXTRAS_USER, userQuery)
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.MAIN_ITEMS));
}
}, v -> { // long click listener
final Object tag = v.getTag();
if (tag instanceof PostModel) {
postsAdapter.isSelecting = true;
toggleSelection((PostModel) tag);
}
return true;
}));
// profileBinding.profileView.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(ProfileViewer.this, PostViewer.class)
// .putExtra(Constants.EXTRAS_INDEX, postModel.getPosition())
// .putExtra(Constants.EXTRAS_POST, postModel)
// .putExtra(Constants.EXTRAS_USER, userQuery)
// .putExtra(Constants.EXTRAS_TYPE, ItemGetType.MAIN_ITEMS));
// }
// }, v -> { // long click listener
// final Object tag = v.getTag();
// if (tag instanceof PostModel) {
// postsAdapter.isSelecting = true;
// toggleSelection((PostModel) tag);
// }
// return true;
// }));
this.lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if ((!autoloadPosts || isHashtag) && hasNextPage) {
@ -279,7 +279,7 @@ public final class ProfileViewer extends BaseLanguageActivity implements SwipeRe
allItems.clear();
selectedItems.clear();
if (postsAdapter != null) {
postsAdapter.isSelecting = false;
// postsAdapter.isSelecting = false;
postsAdapter.notifyDataSetChanged();
}
profileBinding.profileView.appBarLayout.setExpanded(true, true);
@ -776,11 +776,11 @@ public final class ProfileViewer extends BaseLanguageActivity implements SwipeRe
}
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);
// 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);
}
@Override

View File

@ -6,30 +6,36 @@ import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.PostsAdapter;
import awais.instagrabber.asyncs.PostsFetcher;
import awais.instagrabber.asyncs.i.iLikedFetcher;
import awais.instagrabber.customviews.PrimaryActionModeCallback;
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.fragments.main.viewmodels.ProfilePostsViewModel;
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;
@ -48,22 +54,61 @@ public final class SavedViewer extends BaseLanguageActivity implements SwipeRefr
//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<PostModel> selectedItems = new ArrayList<>();
private final ArrayList<PostModel> allItems = new ArrayList<>();
private MenuItem downloadAction;
private ActionMode actionMode;
private ProfilePostsViewModel profilePostsViewModel;
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (postsAdapter == null) {
remove();
return;
}
postsAdapter.clearSelection();
remove();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
R.menu.multi_select_download_menu,
new PrimaryActionModeCallback.CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
onBackPressedCallback.handleOnBackPressed();
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
if (postsAdapter == null || username == null) {
return false;
}
Utils.batchDownload(SavedViewer.this,
username,
DownloadMethod.DOWNLOAD_MAIN,
postsAdapter.getSelectedModels());
checkAndResetAction();
return true;
}
return false;
}
});
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
@Override
public void onResult(final PostModel[] result) {
final int oldSize = allItems.size();
if (result != null) {
allItems.addAll(Arrays.asList(result));
postsAdapter.notifyItemRangeInserted(oldSize, result.length);
final List<PostModel> current = profilePostsViewModel.getList().getValue();
final List<PostModel> resultList = Arrays.asList(result);
if (current == null) {
profilePostsViewModel.getList().postValue(resultList);
} else {
final List<PostModel> currentCopy = new ArrayList<>(current);
currentCopy.addAll(resultList);
profilePostsViewModel.getList().postValue(currentCopy);
}
savedBinding.mainPosts.post(() -> {
savedBinding.mainPosts.setNestedScrollingEnabled(true);
savedBinding.mainPosts.setVisibility(View.VISIBLE);
@ -85,13 +130,12 @@ public final class SavedViewer extends BaseLanguageActivity implements SwipeRefr
}
model.setPageCursor(false, null);
}
}
else {
} else {
savedBinding.swipeRefreshLayout.setRefreshing(false);
if (oldSize == 0) {
Toast.makeText(getApplicationContext(), R.string.empty_list, Toast.LENGTH_SHORT).show();
finish();
}
// if (oldSize == 0) {
// Toast.makeText(getApplicationContext(), R.string.empty_list, Toast.LENGTH_SHORT).show();
// finish();
// }
}
}
};
@ -117,26 +161,36 @@ public final class SavedViewer extends BaseLanguageActivity implements SwipeRefr
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())
profilePostsViewModel = new ViewModelProvider(this).get(ProfilePostsViewModel.class);
postsAdapter = new PostsAdapter((postModel, position) -> {
if (postsAdapter.isSelecting()) {
if (actionMode == null) return;
final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
actionMode.setTitle(title);
return;
}
if (checkAndResetAction()) return;
startActivity(new Intent(this, PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, position)
.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);
}
}, (model, position) -> {
if (!postsAdapter.isSelecting()) {
checkAndResetAction();
return true;
}));
}
final OnBackPressedDispatcher onBackPressedDispatcher = getOnBackPressedDispatcher();
if (onBackPressedDispatcher.hasEnabledCallbacks()) return true;
actionMode = startActionMode(multiSelectAction);
final String title = getString(R.string.number_selected, 1);
actionMode.setTitle(title);
onBackPressedDispatcher.addCallback(onBackPressedCallback);
return true;
});
savedBinding.mainPosts.setAdapter(postsAdapter);
profilePostsViewModel.getList().observe(this, postsAdapter::submitList);
savedBinding.swipeRefreshLayout.setRefreshing(true);
setSupportActionBar(savedBinding.toolbar.toolbar);
savedBinding.toolbar.toolbar.setTitle((action.charAt(0) == '$' ? R.string.saved :
@ -157,19 +211,22 @@ public final class SavedViewer extends BaseLanguageActivity implements SwipeRefr
savedBinding.mainPosts.addOnScrollListener(lazyLoader);
itemGetter = itemGetType -> {
if (itemGetType == ItemGetType.SAVED_ITEMS) return allItems;
if (itemGetType == ItemGetType.SAVED_ITEMS)
return profilePostsViewModel.getList().getValue();
return null;
};
if (action.charAt(0) == '^') new iLikedFetcher(postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else new PostsFetcher(action, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if (action.charAt(0) == '^')
new iLikedFetcher(postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
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);
final MenuItem downloadAction = menu.findItem(R.id.downloadAction);
downloadAction.setVisible(false);
menu.findItem(R.id.favouriteAction).setVisible(false);
@ -183,27 +240,21 @@ public final class SavedViewer extends BaseLanguageActivity implements SwipeRefr
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();
profilePostsViewModel.getList().postValue(Collections.emptyList());
selectedItems.clear();
if (postsAdapter != null) {
postsAdapter.isSelecting = false;
// postsAdapter.isSelecting = false;
postsAdapter.notifyDataSetChanged();
}
savedBinding.swipeRefreshLayout.setRefreshing(true);
if (action.charAt(0) == '^') new iLikedFetcher(postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else new PostsFetcher(action, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if (action.charAt(0) == '^')
new iLikedFetcher(postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
new PostsFetcher(action, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
@ -225,26 +276,13 @@ public final class SavedViewer extends BaseLanguageActivity implements SwipeRefr
}
}
private void toggleSelection(final PostModel postModel) {
if (postModel != null && postsAdapter != null) {
if (postModel.isSelected()) selectedItems.remove(postModel);
else if (selectedItems.size() >= 100) {
Toast.makeText(SavedViewer.this, R.string.downloader_too_many, Toast.LENGTH_SHORT);
return;
}
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);
private boolean checkAndResetAction() {
final OnBackPressedDispatcher onBackPressedDispatcher = getOnBackPressedDispatcher();
if (!onBackPressedDispatcher.hasEnabledCallbacks() || actionMode == null) {
return false;
}
actionMode.finish();
actionMode = null;
return true;
}
}

View File

@ -1,769 +1,167 @@
package awais.instagrabber.activities;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
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;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.UUID;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.adapters.StoriesAdapter;
import awais.instagrabber.asyncs.DownloadAsync;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
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.PostModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class StoryViewer extends BaseLanguageActivity {
private final StoriesAdapter storiesAdapter = new StoriesAdapter(null, new View.OnClickListener() {
@Override
public void onClick(final View v) {
final Object tag = v.getTag();
if (tag instanceof StoryModel) {
currentStory = (StoryModel) tag;
slidePos = currentStory.getPosition();
refreshStory();
}
}
});
private ActivityStoryViewerBinding storyViewerBinding;
private StoryModel[] storyModels;
private GestureDetectorCompat gestureDetector;
private SimpleExoPlayer player;
private SwipeEvent swipeEvent;
private MenuItem menuDownload, menuDm;
private PollModel poll;
private QuestionModel question;
private String[] mentions;
private QuizModel quiz;
private StoryModel currentStory;
private String url, username;
private int slidePos = 0, lastSlidePos = 0;
private final String cookie = settingsHelper.getString(Constants.COOKIE);
private boolean fetching = false;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
storyViewerBinding = ActivityStoryViewerBinding.inflate(getLayoutInflater());
setContentView(storyViewerBinding.getRoot());
setSupportActionBar(storyViewerBinding.toolbar.toolbar);
final Intent intent = getIntent();
if (intent == null || !intent.hasExtra(Constants.EXTRAS_STORIES)
|| (storyModels = (StoryModel[]) intent.getSerializableExtra(Constants.EXTRAS_STORIES)) == null) {
Utils.errorFinish(this);
return;
}
username = intent.getStringExtra(Constants.EXTRAS_USERNAME);
final String highlight = intent.getStringExtra(Constants.EXTRAS_HIGHLIGHT);
final boolean hasUsername = !Utils.isEmpty(username);
final boolean hasHighlight = !Utils.isEmpty(highlight);
if (hasUsername) {
username = username.replace("@", "");
storyViewerBinding.toolbar.toolbar.setTitle(username);
storyViewerBinding.toolbar.toolbar.setOnClickListener(v -> {
searchUsername(username);
});
if (hasHighlight) storyViewerBinding.toolbar.toolbar.setSubtitle(getString(R.string.title_highlight, highlight));
else storyViewerBinding.toolbar.toolbar.setSubtitle(R.string.title_user_story);
}
storyViewerBinding.storiesList.setVisibility(View.GONE);
storyViewerBinding.storiesList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
storyViewerBinding.storiesList.setAdapter(storiesAdapter);
swipeEvent = new SwipeEvent() {
private final int storiesLen = storyModels != null ? storyModels.length : 0;
@Override
public void onSwipe(final boolean isRightSwipe) {
if (storyModels != null && storiesLen > 0) {
if (((slidePos + 1 >= storiesLen && isRightSwipe == false) || (slidePos == 0 && isRightSwipe == true))
&& intent.hasExtra(Constants.FEED)) {
final FeedStoryModel[] storyFeed = (FeedStoryModel[]) intent.getSerializableExtra(Constants.FEED);
final int index = intent.getIntExtra(Constants.FEED_ORDER, 1738);
if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction().execute();
if ((isRightSwipe == true && index == 0) || (isRightSwipe == false && index == storyFeed.length - 1))
Toast.makeText(getApplicationContext(), R.string.no_more_stories, Toast.LENGTH_SHORT).show();
else {
final FeedStoryModel feedStoryModel = isRightSwipe ?
(index == 0 ? null : storyFeed[index - 1]) :
(storyFeed.length == index + 1 ? null : storyFeed[index + 1]);
if (feedStoryModel != null) {
if (fetching) {
Toast.makeText(getApplicationContext(), R.string.be_patient, Toast.LENGTH_SHORT).show();
} else {
fetching = true;
new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> {
if (result != null && result.length > 0) {
final Intent newIntent = new Intent(getApplicationContext(), StoryViewer.class)
.putExtra(Constants.EXTRAS_STORIES, result)
.putExtra(Constants.EXTRAS_USERNAME, feedStoryModel.getProfileModel().getUsername())
.putExtra(Constants.FEED, storyFeed)
.putExtra(Constants.FEED_ORDER, isRightSwipe ? (index - 1) : (index + 1));
newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(newIntent);
} else
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}
}
else {
if (isRightSwipe) {
if (--slidePos <= 0) slidePos = 0;
} else if (++slidePos >= storiesLen) slidePos = storiesLen - 1;
currentStory = storyModels[slidePos];
refreshStory();
}
}
}
};
gestureDetector = new GestureDetectorCompat(this, new SwipeGestureListener(swipeEvent));
viewPost();
}
@SuppressLint("ClickableViewAccessibility")
private void viewPost() {
lastSlidePos = 0;
storyViewerBinding.storiesList.setVisibility(View.GONE);
storiesAdapter.setData(null);
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
storyViewerBinding.playerView.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
storyViewerBinding.imageViewer.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> {
final float diffX = e2.getX() - e1.getX();
try {
if (Math.abs(diffX) > Math.abs(e2.getY() - e1.getY()) && Math.abs(diffX) > SWIPE_THRESHOLD
&& Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
swipeEvent.onSwipe(diffX > 0);
return true;
}
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ACTIVITY_STORY_VIEWER, "viewPost",
new Pair<>("swipeEvent", swipeEvent),
new Pair<>("diffX", diffX));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
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(), tag.toString().matches("^[\\d]+$"))));
});
final View.OnClickListener storyActionListener = 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) -> {
if (!Utils.isEmpty(cookie)) new VoteAction().execute(w);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
else if (tag instanceof QuestionModel) {
question = (QuestionModel) tag;
final EditText input = new EditText(this);
input.setHint(R.string.answer_hint);
new AlertDialog.Builder(this).setTitle(question.getQuestion())
.setView(input)
.setPositiveButton(R.string.ok, (d,w) -> {
new RespondAction().execute(input.getText().toString());
})
.setNegativeButton(R.string.cancel, null)
.show();
}
else if (tag instanceof String[]) {
mentions = (String[]) tag;
new AlertDialog.Builder(this).setTitle(R.string.story_mentions)
.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mentions), (d,w) -> {
searchUsername(mentions[w]);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
else if (tag instanceof QuizModel) {
quiz = (QuizModel) quiz;
String[] choices = new String[quiz.getChoices().length];
for (int q = 0; q < choices.length; ++q) {
choices[q] = (quiz.getMyChoice() == q ? "" :"") + quiz.getChoices()[q]+ " (" + quiz.getCounts()[q] + ")";
}
new AlertDialog.Builder(this).setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion())
.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, choices), (d,w) -> {
if (quiz.getMyChoice() == -1 && !Utils.isEmpty(cookie)) new QuizAction().execute(w);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
};
storyViewerBinding.poll.setOnClickListener(storyActionListener);
storyViewerBinding.answer.setOnClickListener(storyActionListener);
storyViewerBinding.mention.setOnClickListener(storyActionListener);
storyViewerBinding.quiz.setOnClickListener(storyActionListener);
storiesAdapter.setData(storyModels);
if (storyModels.length > 1) storyViewerBinding.storiesList.setVisibility(View.VISIBLE);
currentStory = storyModels[0];
refreshStory();
}
private void setupVideo() {
storyViewerBinding.playerView.setVisibility(View.VISIBLE);
storyViewerBinding.progressView.setVisibility(View.GONE);
storyViewerBinding.imageViewer.setVisibility(View.GONE);
storyViewerBinding.imageViewer.setImageDrawable(null);
player = new SimpleExoPlayer.Builder(this).build();
storyViewerBinding.playerView.setPlayer(player);
player.setPlayWhenReady(settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(this, "instagram"))
.createMediaSource(Uri.parse(url));
mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
@Override
public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.VISIBLE);
}
@Override
public void onLoadCanceled(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
storyViewerBinding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadError(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData, final IOException error, final boolean wasCanceled) {
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
storyViewerBinding.progressView.setVisibility(View.GONE);
}
});
player.prepare(mediaSource);
storyViewerBinding.playerView.setOnClickListener(v -> {
if (player != null) {
if (player.getPlaybackState() == Player.STATE_ENDED) player.seekTo(0);
player.setPlayWhenReady(player.getPlaybackState() == Player.STATE_ENDED || !player.isPlaying());
}
});
}
private void setupImage() {
storyViewerBinding.progressView.setVisibility(View.VISIBLE);
storyViewerBinding.playerView.setVisibility(View.GONE);
storyViewerBinding.imageViewer.setImageDrawable(null);
storyViewerBinding.imageViewer.setVisibility(View.VISIBLE);
storyViewerBinding.imageViewer.setZoomable(true);
storyViewerBinding.imageViewer.setZoomTransitionDuration(420);
storyViewerBinding.imageViewer.setMaximumScale(7.2f);
Glide.with(this).load(url).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
storyViewerBinding.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.GONE);
return false;
}
}).into(storyViewerBinding.imageViewer);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
menu.findItem(R.id.action_settings).setVisible(false);
menu.findItem(R.id.action_search).setVisible(false);
menuDownload = menu.findItem(R.id.action_download);
menuDm = menu.findItem(R.id.action_dms);
menuDownload.setVisible(true);
menuDm.setVisible(false);
menuDownload.setOnMenuItemClickListener(item -> {
if (ContextCompat.checkSelfPermission(this, Utils.PERMS[0]) == PackageManager.PERMISSION_GRANTED)
downloadStory();
else
ActivityCompat.requestPermissions(this, Utils.PERMS, 8020);
return true;
});
menuDm.setOnMenuItemClickListener(item -> {
final EditText input = new EditText(this);
input.setHint(R.string.reply_hint);
new AlertDialog.Builder(this).setTitle(R.string.reply_story)
.setView(input)
.setPositiveButton(R.string.ok, (d,w) -> {
new CommentAction().execute(input.getText().toString());
})
.setNegativeButton(R.string.cancel, null)
.show();
return true;
});
return true;
}
@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) downloadStory();
}
@Override
public void onPause() {
super.onPause();
if (Build.VERSION.SDK_INT < 24) releasePlayer();
}
@Override
public void onStop() {
super.onStop();
if (Build.VERSION.SDK_INT >= 24) releasePlayer();
}
private void downloadStory() {
int error = 0;
if (currentStory != null) {
File dir = new File(Environment.getExternalStorageDirectory(), "Download");
if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = settingsHelper.getString(FOLDER_PATH);
if (!Utils.isEmpty(customPath)) dir = new File(customPath);
}
if (settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) && !Utils.isEmpty(username))
dir = new File(dir, username);
if (dir.exists() || dir.mkdirs()) {
final String storyUrl = currentStory.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO ? currentStory.getVideoUrl() : currentStory.getStoryUrl();
final File saveFile = new File(dir, currentStory.getStoryMediaId() + "_" + currentStory.getTimestamp()
+ Utils.getExtensionFromModel(storyUrl, currentStory));
new DownloadAsync(this, storyUrl, saveFile, result -> {
final int toastRes = result != null && result.exists() ? R.string.downloader_complete
: R.string.downloader_error_download_file;
Toast.makeText(this, toastRes, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else error = 1;
} else error = 2;
if (error == 1) Toast.makeText(this, R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();
else if (error == 2) Toast.makeText(this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
private void refreshStory() {
if (storyViewerBinding.storiesList.getVisibility() == View.VISIBLE) {
StoryModel item = storiesAdapter.getItemAt(lastSlidePos);
if (item != null) {
item.setCurrentSlide(false);
storiesAdapter.notifyItemChanged(lastSlidePos, item);
}
item = storiesAdapter.getItemAt(slidePos);
if (item != null) {
item.setCurrentSlide(true);
storiesAdapter.notifyItemChanged(slidePos, item);
}
}
lastSlidePos = slidePos;
final MediaItemType itemType = currentStory.getItemType();
if (menuDownload != null) menuDownload.setVisible(false);
url = itemType == MediaItemType.MEDIA_TYPE_VIDEO ? currentStory.getVideoUrl() : currentStory.getStoryUrl();
final String shortCode = currentStory.getTappableShortCode();
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);
poll = currentStory.getPoll();
storyViewerBinding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE);
storyViewerBinding.poll.setTag(poll);
question = currentStory.getQuestion();
storyViewerBinding.answer.setVisibility((question != null && !Utils.isEmpty(cookie)) ? View.VISIBLE : View.GONE);
storyViewerBinding.answer.setTag(question);
mentions = currentStory.getMentions();
storyViewerBinding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE);
storyViewerBinding.mention.setTag(mentions);
quiz = currentStory.getQuiz();
storyViewerBinding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE);
storyViewerBinding.quiz.setTag(quiz);
releasePlayer();
final Intent intent = getIntent();
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());
});
}
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo();
else setupImage();
if (!intent.hasExtra(Constants.EXTRAS_HIGHLIGHT))
storyViewerBinding.toolbar.toolbar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L)));
if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction().execute();
}
private void searchUsername(final String text) {
startActivity(
new Intent(getApplicationContext(), ProfileViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, text)
);
}
private void releasePlayer() {
if (player != null) {
try { player.stop(true); } catch (Exception ignored) { }
try { player.release(); } catch (Exception ignored) { }
player = null;
}
}
public static int indexOfIntArray(Object[] array, Object key) {
int returnvalue = -1;
for (int i = 0; i < array.length; ++i) {
if (key == array[i]) {
returnvalue = i;
break;
}
}
return returnvalue;
}
class VoteAction extends AsyncTask<Integer, Void, Void> {
int ok = -1;
String action;
protected Void doInBackground(Integer... rawchoice) {
int choice = rawchoice[0];
final String url = "https://www.instagram.com/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+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", 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;
}
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();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class QuizAction extends AsyncTask<Integer, Void, Void> {
int ok = -1;
String action;
protected Void doInBackground(Integer... rawchoice) {
int choice = rawchoice[0];
final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+quiz.getId()+"/story_quiz_answer/";
try {
JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+"\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+"\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\"}");
ogbody.put("answer", String.valueOf(choice));
String urlParameters = Utils.sign(ogbody.toString());
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
Log.d("austin_debug", "quiz: "+url+" "+cookie+" "+urlParameters);
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = choice;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "quiz: " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok > -1) {
quiz.setMyChoice(ok);
Toast.makeText(getApplicationContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class RespondAction extends AsyncTask<String, Void, Void> {
boolean ok = false;
String action;
protected Void doInBackground(String... rawchoice) {
final String url = "https://i.instagram.com/api/v1/media/"
+currentStory.getStoryMediaId().split("_")[0]+"/"+question.getId()+"/story_question_response/";
try {
JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+"\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+"\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\"}");
String choice = rawchoice[0].replaceAll("\"", ("\\\""));
ogbody.put("response", choice);
String urlParameters = Utils.sign(ogbody.toString());
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "respond: " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (ok) {
Toast.makeText(getApplicationContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class SeenAction extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
final String url = "https://www.instagram.com/stories/reel/seen";
try {
String urlParameters = "reelMediaId="+currentStory.getStoryMediaId().split("_")[0]
+"&reelMediaOwnerId="+currentStory.getUserId()
+"&reelId="+currentStory.getUserId()
+"&reelMediaTakenAt="+ currentStory.getTimestamp()
+"&viewSeenAt="+ currentStory.getTimestamp();
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
Log.d("austin_debug", urlConnection.getResponseCode() + " " + Utils.readFromConnection(urlConnection));
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "seen: " + ex);
}
return null;
}
}
class CommentAction extends AsyncTask<String, Void, Void> {
protected Void doInBackground(String... rawAction) {
final String action = rawAction[0];
final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/";
try {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setUseCaches(false);
final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\",\"recipient_users\":\"["+currentStory.getUserId() // <- string of array of number (not joking)
+"]\"}");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "" + urlParameters.getBytes().length);
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
final String threadid = new JSONObject(Utils.readFromConnection(urlConnection)).getString("thread_id");
final DirectThreadBroadcaster.StoryReplyBroadcastOptions options =
new DirectThreadBroadcaster.StoryReplyBroadcastOptions(
action,
currentStory.getStoryMediaId(),
currentStory.getUserId()
);
final DirectThreadBroadcaster broadcast = new DirectThreadBroadcaster(threadid);
broadcast.setOnTaskCompleteListener(result -> {
Toast.makeText(getApplicationContext(),
result != null ? R.string.answered_story : R.string.downloader_unknown_error,
Toast.LENGTH_SHORT).show();
});
broadcast.execute(options);
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "reply (CT): " + ex);
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
return null;
}
}
}
// package awais.instagrabber.activities;
//
// import android.content.Intent;
// import android.os.AsyncTask;
// import android.os.Bundle;
// import android.view.MenuItem;
// import android.view.View;
// import android.widget.Toast;
//
// import androidx.annotation.Nullable;
// import androidx.core.view.GestureDetectorCompat;
// import androidx.recyclerview.widget.LinearLayoutManager;
//
// import awais.instagrabber.R;
// import awais.instagrabber.adapters.StoriesAdapter;
// import awais.instagrabber.asyncs.SeenAction;
// import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
// 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.StoryModel;
// import awais.instagrabber.models.stickers.PollModel;
// import awais.instagrabber.models.stickers.QuestionModel;
// import awais.instagrabber.models.stickers.QuizModel;
// import awais.instagrabber.utils.Constants;
// import awais.instagrabber.utils.Utils;
//
// import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
// import static awais.instagrabber.utils.Utils.settingsHelper;
//
// public final class StoryViewer extends BaseLanguageActivity {
// private final StoriesAdapter storiesAdapter = new StoriesAdapter(null, new View.OnClickListener() {
// @Override
// public void onClick(final View v) {
// final Object tag = v.getTag();
// if (tag instanceof StoryModel) {
// currentStory = (StoryModel) tag;
// slidePos = currentStory.getPosition();
// refreshStory();
// }
// }
// });
// private ActivityStoryViewerBinding storyViewerBinding;
// private StoryModel[] storyModels;
// private GestureDetectorCompat gestureDetector;
//
// private SwipeEvent swipeEvent;
// private MenuItem menuDownload, menuDm;
// private PollModel poll;
// private QuestionModel question;
// private String[] mentions;
// private QuizModel quiz;
// private StoryModel currentStory;
// private String url, username;
// private int slidePos = 0, lastSlidePos = 0;
// private final String cookie = settingsHelper.getString(Constants.COOKIE);
// private boolean fetching = false;
//
// @Override
// protected void onCreate(@Nullable final Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// storyViewerBinding = ActivityStoryViewerBinding.inflate(getLayoutInflater());
// setContentView(storyViewerBinding.getRoot());
//
// setSupportActionBar(storyViewerBinding.toolbar.toolbar);
//
// final Intent intent = getIntent();
// if (intent == null || !intent.hasExtra(Constants.EXTRAS_STORIES)
// || (storyModels = (StoryModel[]) intent.getSerializableExtra(Constants.EXTRAS_STORIES)) == null) {
// Utils.errorFinish(this);
// return;
// }
//
// username = intent.getStringExtra(Constants.EXTRAS_USERNAME);
// final String highlight = intent.getStringExtra(Constants.EXTRAS_HIGHLIGHT);
// final boolean hasUsername = !Utils.isEmpty(username);
// final boolean hasHighlight = !Utils.isEmpty(highlight);
//
// if (hasUsername) {
// username = username.replace("@", "");
// storyViewerBinding.toolbar.toolbar.setTitle(username);
// storyViewerBinding.toolbar.toolbar.setOnClickListener(v -> {
// searchUsername(username);
// });
// if (hasHighlight) storyViewerBinding.toolbar.toolbar.setSubtitle(getString(R.string.title_highlight, highlight));
// else storyViewerBinding.toolbar.toolbar.setSubtitle(R.string.title_user_story);
// }
//
// storyViewerBinding.storiesList.setVisibility(View.GONE);
// storyViewerBinding.storiesList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
// storyViewerBinding.storiesList.setAdapter(storiesAdapter);
//
// swipeEvent = new SwipeEvent() {
// private final int storiesLen = storyModels != null ? storyModels.length : 0;
//
// @Override
// public void onSwipe(final boolean isRightSwipe) {
// if (storyModels != null && storiesLen > 0) {
// if (((slidePos + 1 >= storiesLen && isRightSwipe == false) || (slidePos == 0 && isRightSwipe == true))
// && intent.hasExtra(Constants.FEED)) {
// final FeedStoryModel[] storyFeed = (FeedStoryModel[]) intent.getSerializableExtra(Constants.FEED);
// final int index = intent.getIntExtra(Constants.FEED_ORDER, 1738);
// if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction(cookie, storyModel).execute();
// if ((isRightSwipe == true && index == 0) || (isRightSwipe == false && index == storyFeed.length - 1))
// Toast.makeText(getApplicationContext(), R.string.no_more_stories, Toast.LENGTH_SHORT).show();
// else {
// final FeedStoryModel feedStoryModel = isRightSwipe ?
// (index == 0 ? null : storyFeed[index - 1]) :
// (storyFeed.length == index + 1 ? null : storyFeed[index + 1]);
// if (feedStoryModel != null) {
// if (fetching) {
// Toast.makeText(getApplicationContext(), R.string.be_patient, Toast.LENGTH_SHORT).show();
// } else {
// fetching = true;
// new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> {
// if (result != null && result.length > 0) {
// final Intent newIntent = new Intent(getApplicationContext(), StoryViewer.class)
// .putExtra(Constants.EXTRAS_STORIES, result)
// .putExtra(Constants.EXTRAS_USERNAME, feedStoryModel.getProfileModel().getUsername())
// .putExtra(Constants.FEED, storyFeed)
// .putExtra(Constants.FEED_ORDER, isRightSwipe ? (index - 1) : (index + 1));
// newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// startActivity(newIntent);
// } else
// Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
// }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
// }
// }
// }
// }
// else {
// if (isRightSwipe) {
// if (--slidePos <= 0) slidePos = 0;
// } else if (++slidePos >= storiesLen) slidePos = storiesLen - 1;
// currentStory = storyModels[slidePos];
// refreshStory();
// }
// }
// }
// };
// gestureDetector = new GestureDetectorCompat(this, new SwipeGestureListener(swipeEvent));
//
// viewPost();
// }
//
// private void searchUsername(final String text) {
// startActivity(
// new Intent(getApplicationContext(), ProfileViewer.class)
// .putExtra(Constants.EXTRAS_USERNAME, text)
// );
// }
//
//
//
// public static int indexOfIntArray(Object[] array, Object key) {
// int returnvalue = -1;
// for (int i = 0; i < array.length; ++i) {
// if (key == array[i]) {
// returnvalue = i;
// break;
// }
// }
// return returnvalue;
// }
//
// }

View File

@ -1,87 +1,56 @@
package awais.instagrabber.adapters;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import java.util.ArrayList;
import androidx.recyclerview.widget.DiffUtil;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.DiscoverViewHolder;
import awais.instagrabber.models.DiscoverItemModel;
import awais.instagrabber.models.enums.MediaItemType;
public final class DiscoverAdapter extends RecyclerView.Adapter<DiscoverViewHolder> {
private final ArrayList<DiscoverItemModel> discoverItemModels;
private final View.OnClickListener clickListener;
private final View.OnLongClickListener longClickListener;
private LayoutInflater layoutInflater;
public boolean isSelecting = false;
public final class DiscoverAdapter extends MultiSelectListAdapter<DiscoverItemModel, DiscoverViewHolder> {
public DiscoverAdapter(final ArrayList<DiscoverItemModel> discoverItemModels, final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
this.discoverItemModels = discoverItemModels;
this.longClickListener = longClickListener;
this.clickListener = clickListener;
private static final DiffUtil.ItemCallback<DiscoverItemModel> diffCallback = new DiffUtil.ItemCallback<DiscoverItemModel>() {
@Override
public boolean areItemsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final DiscoverItemModel oldItem, @NonNull final DiscoverItemModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public DiscoverAdapter(final OnItemClickListener<DiscoverItemModel> clickListener,
final OnItemLongClickListener<DiscoverItemModel> longClickListener) {
super(diffCallback, clickListener, longClickListener);
}
@NonNull
@Override
public DiscoverViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
return new DiscoverViewHolder(layoutInflater.inflate(R.layout.item_post, parent, false));
}
@Override
public void onBindViewHolder(@NonNull final DiscoverViewHolder holder, final int position) {
final DiscoverItemModel itemModel = discoverItemModels.get(position);
final DiscoverItemModel itemModel = getItem(position);
if (itemModel != null) {
itemModel.setPosition(position);
holder.itemView.setTag(itemModel);
holder.itemView.setOnClickListener(clickListener);
holder.itemView.setOnLongClickListener(longClickListener);
holder.itemView.setOnClickListener(v -> internalOnItemClickListener.onItemClick(itemModel, position));
holder.itemView.setOnLongClickListener(v -> internalOnLongItemClickListener.onItemLongClick(itemModel, position));
final MediaItemType mediaType = itemModel.getItemType();
holder.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER
? View.VISIBLE : View.GONE);
holder.typeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE);
holder.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? R.drawable.slider : R.drawable.video);
holder.selectedView.setVisibility(itemModel.isSelected() ? View.VISIBLE : View.GONE);
holder.progressView.setVisibility(View.VISIBLE);
Glide.with(layoutInflater.getContext()).load(itemModel.getDisplayUrl()).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
return false;
}
}).into(holder.postImage);
holder.postImage.setImageURI(itemModel.getDisplayUrl());
}
}
@Override
public int getItemCount() {
return discoverItemModels == null ? 0 : discoverItemModels.size();
}
}

View File

@ -9,9 +9,6 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import com.bumptech.glide.RequestManager;
import com.google.android.exoplayer2.SimpleExoPlayer;
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedPhotoViewHolder;
import awais.instagrabber.adapters.viewholder.feed.FeedSliderViewHolder;
@ -27,11 +24,8 @@ import awais.instagrabber.utils.Utils;
public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder> {
private static final String TAG = "FeedAdapter";
// private final static String ellipsize = "… more";
private final RequestManager glide;
private final View.OnClickListener clickListener;
private final MentionClickListener mentionClickListener;
public SimpleExoPlayer pagerPlayer;
private final View.OnLongClickListener longClickListener = v -> {
final Object tag;
if (v instanceof RamboTextView && (tag = v.getTag()) instanceof FeedModel)
@ -51,11 +45,10 @@ public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder
}
};
public FeedAdapter(final RequestManager glide,
final View.OnClickListener clickListener,
public FeedAdapter(final View.OnClickListener clickListener,
final MentionClickListener mentionClickListener) {
super(diffCallback);
this.glide = glide;
// private final static String ellipsize = "… more";
this.clickListener = clickListener;
this.mentionClickListener = mentionClickListener;
}
@ -78,7 +71,7 @@ public final class FeedAdapter extends ListAdapter<FeedModel, FeedItemViewHolder
default:
case MEDIA_TYPE_IMAGE: {
final ItemFeedPhotoBinding binding = ItemFeedPhotoBinding.inflate(layoutInflater, parent, false);
return new FeedPhotoViewHolder(binding, glide, mentionClickListener, clickListener, longClickListener);
return new FeedPhotoViewHolder(binding, mentionClickListener, clickListener, longClickListener);
}
}
}

View File

@ -1,55 +1,51 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.HighlightViewHolder;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
public final class FeedStoriesAdapter extends RecyclerView.Adapter<HighlightViewHolder> {
private final View.OnClickListener clickListener;
private LayoutInflater layoutInflater;
private FeedStoryModel[] feedStoryModels;
public final class FeedStoriesAdapter extends ListAdapter<FeedStoryModel, HighlightViewHolder> {
private final OnFeedStoryClickListener listener;
public FeedStoriesAdapter(final FeedStoryModel[] feedStoryModels, final View.OnClickListener clickListener) {
this.feedStoryModels = feedStoryModels;
this.clickListener = clickListener;
private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() {
@Override
public boolean areItemsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
@Override
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
};
public FeedStoriesAdapter(final OnFeedStoryClickListener listener) {
super(diffCallback);
this.listener = listener;
}
@NonNull
@Override
public HighlightViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
return new HighlightViewHolder(layoutInflater.inflate(R.layout.item_highlight, parent, false));
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemHighlightBinding binding = ItemHighlightBinding.inflate(layoutInflater, parent, false);
return new HighlightViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final HighlightViewHolder holder, final int position) {
final FeedStoryModel feedStoryModel = feedStoryModels[position];
if (feedStoryModel != null) {
holder.itemView.setTag(feedStoryModel);
holder.itemView.setOnClickListener(clickListener);
final ProfileModel profileModel = feedStoryModel.getProfileModel();
holder.title.setText(profileModel.getUsername());
holder.icon.setImageURI(profileModel.getSdProfilePic());
holder.icon.setAlpha(feedStoryModel.getFullyRead() ? 0.5F : 1.0F);
holder.title.setAlpha(feedStoryModel.getFullyRead() ? 0.5F : 1.0F);
}
final FeedStoryModel model = getItem(position);
holder.bind(model, position, listener);
}
public void setData(final FeedStoryModel[] feedStoryModels) {
this.feedStoryModels = feedStoryModels;
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return feedStoryModels == null ? 0 : feedStoryModels.length;
public interface OnFeedStoryClickListener {
void onFeedStoryClick(FeedStoryModel model, int position);
}
}

View File

@ -27,18 +27,19 @@ public final class HighlightsAdapter extends RecyclerView.Adapter<HighlightViewH
@Override
public HighlightViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
return new HighlightViewHolder(layoutInflater.inflate(R.layout.item_highlight, parent, false));
// return new HighlightViewHolder(layoutInflater.inflate(R.layout.item_highlight, parent, false));
return null;
}
@Override
public void onBindViewHolder(@NonNull final HighlightViewHolder holder, final int position) {
final HighlightModel highlightModel = highlightModels[position];
if (highlightModel != null) {
holder.itemView.setTag(highlightModel);
holder.itemView.setOnClickListener(clickListener);
holder.title.setText(highlightModel.getTitle());
Glide.with(holder.itemView).load(highlightModel.getThumbnailUrl()).into(holder.icon);
}
// final HighlightModel highlightModel = highlightModels[position];
// if (highlightModel != null) {
// holder.itemView.setTag(highlightModel);
// holder.itemView.setOnClickListener(clickListener);
// holder.title.setText(highlightModel.getTitle());
// Glide.with(holder.itemView).load(highlightModel.getThumbnailUrl()).into(holder.icon);
// }
}
public void setData(final HighlightModel[] highlightModels) {

View File

@ -0,0 +1,106 @@
package awais.instagrabber.adapters;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public abstract class MultiSelectListAdapter<T extends MultiSelectListAdapter.Selectable, VH extends RecyclerView.ViewHolder> extends ListAdapter<T, VH> {
private boolean isSelecting = false;
private OnItemClickListener<T> clickListener;
private OnItemLongClickListener<T> longClickListener;
private final List<T> selectedItems = new ArrayList<>();
protected final OnItemClickListener<T> internalOnItemClickListener = (item, position) -> {
if (isSelecting) {
toggleSelection(item, position);
}
if (clickListener == null) {
return;
}
clickListener.onItemClick(item, position);
};
protected final OnItemLongClickListener<T> internalOnLongItemClickListener = (item, position) -> {
if (!isSelecting) {
isSelecting = true;
}
toggleSelection(item, position);
if (longClickListener == null) {
return true;
}
return longClickListener.onItemLongClick(item, position);
};
protected MultiSelectListAdapter(@NonNull final DiffUtil.ItemCallback<T> diffCallback,
final OnItemClickListener<T> clickListener,
final OnItemLongClickListener<T> longClickListener) {
super(diffCallback);
this.clickListener = clickListener;
this.longClickListener = longClickListener;
}
protected MultiSelectListAdapter(@NonNull final AsyncDifferConfig<T> config,
final OnItemClickListener<T> clickListener,
final OnItemLongClickListener<T> longClickListener) {
super(config);
this.clickListener = clickListener;
this.longClickListener = longClickListener;
}
private void toggleSelection(final T item, final int position) {
if (item == null) {
return;
}
if (selectedItems.size() >= 100) {
// Toast.makeText(mainActivity, R.string.downloader_too_many, Toast.LENGTH_SHORT);
return;
}
if (item.isSelected()) {
item.setSelected(false);
selectedItems.remove(item);
} else {
item.setSelected(true);
selectedItems.add(item);
}
if (selectedItems.size() == 0) {
isSelecting = false;
}
notifyItemChanged(position);
}
public boolean isSelecting() {
return isSelecting;
}
public List<T> getSelectedModels() {
return selectedItems;
}
public void clearSelection() {
for (final T item : selectedItems) {
item.setSelected(false);
}
selectedItems.clear();
isSelecting = false;
notifyDataSetChanged();
}
public interface Selectable {
boolean isSelected();
void setSelected(boolean selected);
}
public interface OnItemClickListener<T> {
void onItemClick(T item, int position);
}
public interface OnItemLongClickListener<T> {
boolean onItemLongClick(T item, int position);
}
}

View File

@ -1,92 +1,45 @@
package awais.instagrabber.adapters;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.DiffUtil;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import java.util.ArrayList;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.PostViewHolder;
import awais.instagrabber.databinding.ItemPostBinding;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.MediaItemType;
public final class PostsAdapter extends RecyclerView.Adapter<PostViewHolder> {
private final ArrayList<PostModel> postModels;
private final View.OnClickListener clickListener;
private final View.OnLongClickListener longClickListener;
private LayoutInflater layoutInflater;
public boolean isSelecting = false;
public final class PostsAdapter extends MultiSelectListAdapter<PostModel, PostViewHolder> {
public PostsAdapter(final ArrayList<PostModel> postModels, final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
this.postModels = postModels;
this.clickListener = clickListener;
this.longClickListener = longClickListener;
private static final DiffUtil.ItemCallback<PostModel> diffCallback = new DiffUtil.ItemCallback<PostModel>() {
@Override
public boolean areItemsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
@Override
public boolean areContentsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) {
return oldItem.getPostId().equals(newItem.getPostId());
}
};
public PostsAdapter(final OnItemClickListener<PostModel> clickListener,
final OnItemLongClickListener<PostModel> longClickListener) {
super(diffCallback, clickListener, longClickListener);
}
@NonNull
@Override
public PostViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext());
return new PostViewHolder(layoutInflater.inflate(R.layout.item_post, parent, false));
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemPostBinding binding = ItemPostBinding.inflate(layoutInflater, parent, false);
return new PostViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final PostViewHolder holder, final int position) {
final PostModel postModel = postModels.get(position);
if (postModel != null) {
postModel.setPosition(position);
holder.itemView.setTag(postModel);
holder.itemView.setOnClickListener(clickListener);
holder.itemView.setOnLongClickListener(longClickListener);
final MediaItemType itemType = postModel.getItemType();
final boolean isSlider = itemType == MediaItemType.MEDIA_TYPE_SLIDER;
holder.isDownloaded.setVisibility(postModel.isDownloaded() ? View.VISIBLE : View.GONE);
holder.typeIcon.setVisibility(itemType == MediaItemType.MEDIA_TYPE_VIDEO || isSlider ? View.VISIBLE : View.GONE);
holder.typeIcon.setImageResource(isSlider ? R.drawable.slider : R.drawable.video);
holder.selectedView.setVisibility(postModel.isSelected() ? View.VISIBLE : View.GONE);
holder.progressView.setVisibility(View.VISIBLE);
final RequestManager glideRequestManager = Glide.with(holder.postImage);
glideRequestManager.load(postModel.getThumbnailUrl()).listener(new RequestListener<Drawable>() {
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
return false;
}
@Override
public boolean onLoadFailed(@Nullable final GlideException e, final Object model, final Target<Drawable> target, final boolean isFirstResource) {
holder.progressView.setVisibility(View.GONE);
// glideRequestManager.load(postModel.getDisplayUrl()).into(holder.postImage);
return false;
}
}).into(holder.postImage);
}
}
@Override
public int getItemCount() {
return postModels == null ? 0 : postModels.size();
final PostModel postModel = getItem(position);
holder.bind(postModel, position, internalOnItemClickListener, internalOnLongItemClickListener);
}
}

View File

@ -1,84 +1,80 @@
package awais.instagrabber.adapters;
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemStoryBinding;
import awais.instagrabber.models.StoryModel;
public final class StoriesAdapter extends RecyclerView.Adapter<StoriesAdapter.StoryViewHolder> {
private final View.OnClickListener clickListener;
private LayoutInflater layoutInflater;
private StoryModel[] storyModels;
private Resources resources;
private int width, height;
public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter.StoryViewHolder> {
private final OnItemClickListener onItemClickListener;
public StoriesAdapter(final StoryModel[] storyModels, final View.OnClickListener clickListener) {
this.storyModels = storyModels;
this.clickListener = clickListener;
private static final DiffUtil.ItemCallback<StoryModel> diffCallback = new DiffUtil.ItemCallback<StoryModel>() {
@Override
public boolean areItemsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
@Override
public boolean areContentsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
};
public StoriesAdapter(final OnItemClickListener onItemClickListener) {
super(diffCallback);
this.onItemClickListener = onItemClickListener;
}
@NonNull
@Override
public StoryViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final Context context = parent.getContext();
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context);
if (resources == null) resources = context.getResources();
height = Math.round(resources.getDimension(R.dimen.story_item_height));
width = Math.round(resources.getDimension(R.dimen.story_item_width));
return new StoryViewHolder(layoutInflater.inflate(R.layout.item_story, parent, false));
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemStoryBinding binding = ItemStoryBinding.inflate(layoutInflater, parent, false);
return new StoryViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final StoryViewHolder holder, final int position) {
final StoryModel storyModel = storyModels[position];
if (storyModel != null) {
storyModel.setPosition(position);
holder.itemView.setTag(storyModel);
holder.itemView.setOnClickListener(clickListener);
holder.selectedView.setVisibility(storyModel.isCurrentSlide() ? View.VISIBLE : View.GONE);
Glide.with(holder.itemView).load(storyModel.getStoryUrl())
.apply(new RequestOptions().override(width, height))
.into(holder.icon);
}
}
public void setData(final StoryModel[] storyModels) {
this.storyModels = storyModels;
notifyDataSetChanged();
}
public StoryModel getItemAt(final int position) {
return storyModels == null ? null : storyModels[position];
}
@Override
public int getItemCount() {
return storyModels == null ? 0 : storyModels.length;
final StoryModel storyModel = getItem(position);
holder.bind(storyModel, position, onItemClickListener);
}
public final static class StoryViewHolder extends RecyclerView.ViewHolder {
public final ImageView icon, selectedView;
private final ItemStoryBinding binding;
public StoryViewHolder(@NonNull final View itemView) {
super(itemView);
selectedView = itemView.findViewById(R.id.selectedView);
icon = itemView.findViewById(R.id.icon);
public StoryViewHolder(final ItemStoryBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final StoryModel model,
final int position,
final OnItemClickListener clickListener) {
if (model == null) return;
model.setPosition(position);
itemView.setTag(model);
itemView.setOnClickListener(v -> {
if (clickListener == null) return;
clickListener.onItemClick(model, position);
});
binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE);
binding.icon.setImageURI(model.getStoryUrl());
// Glide.with(itemView).load(model.getStoryUrl())
// .apply(new RequestOptions().override(width, height))
// .into(holder.icon);
}
}
public interface OnItemClickListener {
void onItemClick(StoryModel storyModel, int position);
}
}

View File

@ -6,17 +6,21 @@ import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import awais.instagrabber.R;
public final class DiscoverViewHolder extends RecyclerView.ViewHolder {
public final ImageView postImage, typeIcon;
public final View selectedView, progressView;
public final SimpleDraweeView postImage;
public final ImageView typeIcon;
public final View selectedView;
// public final View progressView;
public DiscoverViewHolder(@NonNull final View itemView) {
super(itemView);
typeIcon = itemView.findViewById(R.id.typeIcon);
postImage = itemView.findViewById(R.id.postImage);
selectedView = itemView.findViewById(R.id.selectedView);
progressView = itemView.findViewById(R.id.progressView);
// progressView = itemView.findViewById(R.id.progressView);
}
}

View File

@ -1,22 +1,33 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.customviews.CircularImageView;
import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.databinding.ItemHighlightBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
public final class HighlightViewHolder extends RecyclerView.ViewHolder {
public final CircularImageView icon;
public final TextView title;
public HighlightViewHolder(@NonNull final View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.icon);
title = itemView.findViewById(R.id.title);
private final ItemHighlightBinding binding;
public HighlightViewHolder(final ItemHighlightBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final FeedStoryModel model,
final int position,
final FeedStoriesAdapter.OnFeedStoryClickListener listener) {
if (model == null) return;
binding.getRoot().setOnClickListener(v -> {
if (listener == null) return;
listener.onFeedStoryClick(model, position);
});
final ProfileModel profileModel = model.getProfileModel();
binding.title.setText(profileModel.getUsername());
binding.title.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
binding.icon.setImageURI(profileModel.getSdProfilePic());
binding.icon.setAlpha(model.getFullyRead() ? 0.5F : 1.0F);
}
}

View File

@ -1,23 +1,43 @@
package awais.instagrabber.adapters.viewholder;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.MultiSelectListAdapter.OnItemClickListener;
import awais.instagrabber.adapters.MultiSelectListAdapter.OnItemLongClickListener;
import awais.instagrabber.databinding.ItemPostBinding;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.enums.MediaItemType;
public final class PostViewHolder extends RecyclerView.ViewHolder {
public final ImageView postImage, typeIcon;
public final View selectedView, progressView, isDownloaded;
private final ItemPostBinding binding;
public PostViewHolder(@NonNull final View itemView) {
super(itemView);
typeIcon = itemView.findViewById(R.id.typeIcon);
postImage = itemView.findViewById(R.id.postImage);
isDownloaded = itemView.findViewById(R.id.isDownloaded);
selectedView = itemView.findViewById(R.id.selectedView);
progressView = itemView.findViewById(R.id.progressView);
public PostViewHolder(@NonNull final ItemPostBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final PostModel postModel,
final int position,
final OnItemClickListener<PostModel> clickListener,
final OnItemLongClickListener<PostModel> longClickListener) {
if (postModel == null) return;
postModel.setPosition(position);
itemView.setOnClickListener(v -> clickListener.onItemClick(postModel, position));
itemView.setOnLongClickListener(v -> longClickListener.onItemLongClick(postModel, position));
final MediaItemType itemType = postModel.getItemType();
final boolean isSlider = itemType == MediaItemType.MEDIA_TYPE_SLIDER;
binding.isDownloaded.setVisibility(postModel.isDownloaded() ? View.VISIBLE : View.GONE);
binding.typeIcon.setVisibility(itemType == MediaItemType.MEDIA_TYPE_VIDEO || isSlider ? View.VISIBLE : View.GONE);
binding.typeIcon.setImageResource(isSlider ? R.drawable.slider : R.drawable.video);
binding.selectedView.setVisibility(postModel.isSelected() ? View.VISIBLE : View.GONE);
binding.postImage.setImageURI(postModel.getThumbnailUrl());
}
}

View File

@ -11,8 +11,6 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.RequestManager;
import org.json.JSONObject;
import awais.instagrabber.R;
@ -31,8 +29,6 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
private final ItemFeedBottomBinding bottomBinding;
private final MentionClickListener mentionClickListener;
boolean captionExpanded = false;
public FeedItemViewHolder(@NonNull final View root,
final ItemFeedTopBinding topBinding,
final ItemFeedBottomBinding bottomBinding,
@ -64,9 +60,6 @@ public abstract class FeedItemViewHolder extends RecyclerView.ViewHolder {
bottomBinding.btnComments.setTag(feedModel);
final ProfileModel profileModel = feedModel.getProfileModel();
if (profileModel != null) {
// glide.load(profileModel.getSdProfilePic())
// .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
// .into(topBinding.ivProfilePic);
topBinding.ivProfilePic.setImageURI(profileModel.getSdProfilePic());
final int titleLen = profileModel.getUsername().length() + 1;
final SpannableString spannableString = new SpannableString("@" + profileModel.getUsername());

View File

@ -1,14 +1,11 @@
package awais.instagrabber.adapters.viewholder.feed;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.bumptech.glide.RequestManager;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
@ -25,22 +22,15 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
private static final String TAG = "FeedPhotoViewHolder";
private final ItemFeedPhotoBinding binding;
private final RequestManager glide;
private final ColorDrawable drawable;
// private final PipelineDraweeControllerBuilder controllerBuilder;
// private final CustomTarget<Bitmap> customTarget;
public FeedPhotoViewHolder(@NonNull final ItemFeedPhotoBinding binding,
final RequestManager glide,
final MentionClickListener mentionClickListener,
final View.OnClickListener clickListener,
final View.OnLongClickListener longClickListener) {
super(binding.getRoot(), binding.itemFeedTop, binding.itemFeedBottom, mentionClickListener, clickListener, longClickListener);
this.binding = binding;
this.glide = glide;
binding.itemFeedBottom.videoViewsContainer.setVisibility(View.GONE);
binding.itemFeedBottom.btnMute.setVisibility(View.GONE);
drawable = new ColorDrawable(Color.WHITE);
binding.imageViewer.setAllowTouchInterceptionWhileZoomed(false);
final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(itemView.getContext().getResources())
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
@ -50,15 +40,13 @@ public class FeedPhotoViewHolder extends FeedItemViewHolder {
@Override
public void bindItem(final FeedModel feedModel) {
// glide.clear(customTarget);
if (feedModel == null) {
return;
}
final ViewGroup.LayoutParams layoutParams = binding.imageViewer.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels;
final int resultingHeight = Utils.getResultingHeight(requiredWidth, feedModel.getImageHeight(), feedModel.getImageWidth());
layoutParams.width = requiredWidth;
layoutParams.height = resultingHeight;
layoutParams.width = feedModel.getImageWidth() == 0 ? requiredWidth : feedModel.getImageWidth();
layoutParams.height = feedModel.getImageHeight() == 0 ? requiredWidth + 1 : feedModel.getImageHeight();
binding.imageViewer.requestLayout();
final String thumbnailUrl = feedModel.getThumbnailUrl();
String url = feedModel.getDisplayUrl();

View File

@ -151,14 +151,14 @@ public class FeedSliderViewHolder extends FeedItemViewHolder {
final SimpleExoPlayer player = (SimpleExoPlayer) tag;
final float intVol = player.getVolume() == 0f ? 1f : 0f;
player.setVolume(intVol);
binding.itemFeedBottom.btnMute.setImageResource(intVol == 0f ? R.drawable.vol : R.drawable.mute);
binding.itemFeedBottom.btnMute.setImageResource(intVol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
Utils.sessionVolumeFull = intVol == 1f;
};
final ViewerPostModel firstItem = sliderItems[0];
if (firstItem.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO) {
binding.itemFeedBottom.btnMute.setVisibility(View.VISIBLE);
}
binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.mute : R.drawable.vol);
binding.itemFeedBottom.btnMute.setImageResource(Utils.sessionVolumeFull ? R.drawable.ic_volume_off_24 : R.drawable.ic_volume_up_24);
binding.itemFeedBottom.btnMute.setOnClickListener(muteClickListener);
}

View File

@ -74,8 +74,9 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
private void setThumbnail(final FeedModel feedModel) {
final ViewGroup.LayoutParams layoutParams = binding.thumbnailParent.getLayoutParams();
layoutParams.width = feedModel.getImageWidth();
layoutParams.height = feedModel.getImageHeight();
final int requiredWidth = Utils.displayMetrics.widthPixels;
layoutParams.width = feedModel.getImageWidth() == 0 ? requiredWidth : feedModel.getImageWidth();
layoutParams.height = feedModel.getImageHeight() == 0 ? requiredWidth + 1 : feedModel.getImageHeight();
binding.thumbnailParent.requestLayout();
final ImageRequest thumbnailRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(feedModel.getThumbnailUrl()))
.setProgressiveRenderingEnabled(true)
@ -131,7 +132,7 @@ public class FeedVideoViewHolder extends FeedItemViewHolder {
}
private void setMuteIcon(final float vol) {
binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.vol : R.drawable.mute);
binding.itemFeedBottom.btnMute.setImageResource(vol == 0f ? R.drawable.ic_volume_up_24 : R.drawable.ic_volume_off_24);
}
public FeedModel getCurrentFeedModel() {

View File

@ -0,0 +1,81 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class CommentAction extends AsyncTask<String, Void, String> {
private static final String TAG = "CommentAction";
private final String cookie;
private final StoryModel storyModel;
private final OnTaskCompleteListener onTaskCompleteListener;
public CommentAction(final String cookie, final StoryModel storyModel, final OnTaskCompleteListener onTaskCompleteListener) {
this.cookie = cookie;
this.storyModel = storyModel;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected String doInBackground(String... rawAction) {
final String action = rawAction[0];
final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/";
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setUseCaches(false);
final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+ "\",\"recipient_users\":\"[" + storyModel.getUserId() // <- string of array of number (not joking)
+ "]\"}");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if (urlParameters != null) {
urlConnection.setRequestProperty("Content-Length", "" + urlParameters.getBytes().length);
}
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
return new JSONObject(Utils.readFromConnection(urlConnection)).getString("thread_id");
}
} catch (Throwable ex) {
Log.e(TAG, "reply (CT): " + ex);
// Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
@Override
protected void onPostExecute(final String threadId) {
if (threadId == null || onTaskCompleteListener == null) {
return;
}
onTaskCompleteListener.onTaskComplete(threadId);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final String threadId);
}
}

View File

@ -25,6 +25,7 @@ import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.logCollector;
public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
private static final String TAG = "PostsFetcher";
private final String endCursor;
private final String id;
private final FetchListener<PostModel[]> fetchListener;
@ -87,7 +88,8 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
if (!Utils.isEmpty(customPath)) customDir = new File(customPath);
}
final JSONObject mediaPosts = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data")
final JSONObject mediaPosts = new JSONObject(Utils.readFromConnection(conn))
.getJSONObject("data")
.getJSONObject(isHashTag ? Constants.EXTRAS_HASHTAG :
(isLocation ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_USER))
.getJSONObject(isHashTag ? "edge_hashtag_to_media" :
@ -131,7 +133,7 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
Utils.checkExistence(downloadDir, customDir, isSlider, models[i]);
}
if (models[models.length - 1] != null)
if (models.length != 0 && models[models.length - 1] != null)
models[models.length - 1].setPageCursor(hasNextPage, endCursor);
result = models;
@ -141,7 +143,7 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
} catch (Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_MAIN_POSTS_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
return result;

View File

@ -0,0 +1,88 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class QuizAction extends AsyncTask<Integer, Void, Integer> {
private static final String TAG = "QuizAction";
private final StoryModel storyModel;
private final QuizModel quizModel;
private final String cookie;
private final OnTaskCompleteListener onTaskCompleteListener;
public QuizAction(final StoryModel storyModel,
final QuizModel quizModel,
final String cookie,
final OnTaskCompleteListener onTaskCompleteListener) {
this.storyModel = storyModel;
this.quizModel = quizModel;
this.cookie = cookie;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected Integer doInBackground(Integer... rawChoice) {
int choice = rawChoice[0];
final String url = "https://i.instagram.com/api/v1/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + quizModel.getId() + "/story_quiz_answer/";
HttpURLConnection urlConnection = null;
try {
JSONObject ogBody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+ "\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+ "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+ "\"}");
ogBody.put("answer", String.valueOf(choice));
String urlParameters = Utils.sign(ogBody.toString());
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if (urlParameters != null) {
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
}
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
Log.d(TAG, "quiz: " + url + " " + cookie + " " + urlParameters);
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
return choice;
}
} catch (Throwable ex) {
Log.e(TAG, "quiz: " + ex);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return -1;
}
@Override
protected void onPostExecute(final Integer choice) {
if (onTaskCompleteListener == null || choice == null) return;
onTaskCompleteListener.onTaskComplete(choice);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final int choice);
}
}

View File

@ -0,0 +1,87 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class RespondAction extends AsyncTask<String, Void, Boolean> {
private final StoryModel storyModel;
private final QuestionModel questionModel;
private final String cookie;
private final OnTaskCompleteListener onTaskCompleteListener;
public RespondAction(final StoryModel storyModel,
final QuestionModel questionModel,
final String cookie,
final OnTaskCompleteListener onTaskCompleteListener) {
this.storyModel = storyModel;
this.questionModel = questionModel;
this.cookie = cookie;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected Boolean doInBackground(String... rawChoice) {
final String url = "https://i.instagram.com/api/v1/media/"
+ storyModel.getStoryMediaId().split("_")[0] + "/" + questionModel.getId() + "/story_question_response/";
HttpURLConnection urlConnection = null;
try {
JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+ "\",\"mutation_token\":\"" + UUID.randomUUID().toString()
+ "\",\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+ "\"}");
String choice = rawChoice[0].replaceAll("\"", ("\\\""));
ogbody.put("response", choice);
String urlParameters = Utils.sign(ogbody.toString());
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
return true;
}
} catch (Throwable ex) {
Log.e("austin_debug", "respond: " + ex);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
@Override
protected void onPostExecute(final Boolean ok) {
if (onTaskCompleteListener == null) return;
onTaskCompleteListener.onTaskComplete(ok);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final boolean result);
}
}

View File

@ -0,0 +1,51 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.utils.Utils;
public class SeenAction extends AsyncTask<Void, Void, Void> {
private static final String TAG = "SeenAction";
private final String cookie;
private final StoryModel storyModel;
public SeenAction(final String cookie, final StoryModel storyModel) {
this.cookie = cookie;
this.storyModel = storyModel;
}
protected Void doInBackground(Void... voids) {
final String url = "https://www.instagram.com/stories/reel/seen";
try {
String urlParameters = "reelMediaId=" + storyModel.getStoryMediaId().split("_")[0]
+ "&reelMediaOwnerId=" + storyModel.getUserId()
+ "&reelId=" + storyModel.getUserId()
+ "&reelMediaTakenAt=" + storyModel.getTimestamp()
+ "&viewSeenAt=" + storyModel.getTimestamp();
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
Log.d(TAG, urlConnection.getResponseCode() + " " + Utils.readFromConnection(urlConnection));
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e(TAG, "Error", ex);
}
return null;
}
}

View File

@ -0,0 +1,69 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.utils.Constants;
public class VoteAction extends AsyncTask<Integer, Void, Integer> {
private static final String TAG = "VoteAction";
private final StoryModel storyModel;
private final PollModel pollModel;
private final String cookie;
private final OnTaskCompleteListener onTaskCompleteListener;
public VoteAction(final StoryModel storyModel,
final PollModel pollModel,
final String cookie,
final OnTaskCompleteListener onTaskCompleteListener) {
this.storyModel = storyModel;
this.pollModel = pollModel;
this.cookie = cookie;
this.onTaskCompleteListener = onTaskCompleteListener;
}
protected Integer doInBackground(Integer... rawChoice) {
int choice = rawChoice[0];
final String url = "https://www.instagram.com/media/" + storyModel.getStoryMediaId().split("_")[0] + "/" + pollModel.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", 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) {
return choice;
}
urlConnection.disconnect();
} catch (Exception ex) {
Log.e(TAG, "Error", ex);
}
return -1;
}
@Override
protected void onPostExecute(final Integer result) {
if (result == null || onTaskCompleteListener == null) return;
onTaskCompleteListener.onTaskComplete(result);
}
public interface OnTaskCompleteListener {
void onTaskComplete(final int choice);
}
}

View File

@ -25,11 +25,18 @@ import static awais.instagrabber.utils.Utils.logCollector;
public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[]> {
private final String id;
private String username;
private final boolean isLoc, isHashtag, storiesig, highlight;
private final boolean isLoc;
private final boolean isHashtag;
private final boolean storiesig;
private final boolean highlight;
private final FetchListener<StoryModel[]> fetchListener;
public iStoryStatusFetcher(final String id, final String username, final boolean isLoc,
final boolean isHashtag, final boolean storiesig, final boolean highlight,
public iStoryStatusFetcher(final String id,
final String username,
final boolean isLoc,
final boolean isHashtag,
final boolean storiesig,
final boolean highlight,
final FetchListener<StoryModel[]> fetchListener) {
this.id = id;
this.username = username;
@ -43,9 +50,35 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
@Override
protected StoryModel[] doInBackground(final Void... voids) {
StoryModel[] result = null;
final String url = "https://" + (storiesig ? "storiesig" : "i.instagram") + ".com/api/v1/"
+ (isLoc ? "locations/" : (isHashtag ? "tags/" : (highlight ? "feed/reels_media?user_ids=" : "feed/user/")))
+ id.replace(":", "%3A") + (highlight ? "" : (storiesig ? "/reel_media/" : "/story/"));
final String userId = id.replace(":", "%3A");
final StringBuilder builder = new StringBuilder();
builder.append("https://");
if (storiesig) {
builder.append("storiesig");
} else {
builder.append("i.instagram");
}
builder.append(".com/api/v1/");
if (isLoc) {
builder.append("locations/");
}
if (isHashtag) {
builder.append("tags/");
}
if (highlight) {
builder.append("feed/reels_media?user_ids=");
} else {
builder.append("feed/user/");
}
builder.append(userId);
if (!highlight) {
if (storiesig) {
builder.append("/reel_media/");
} else {
builder.append("/story/");
}
}
final String url = builder.toString();
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setInstanceFollowRedirects(true);
@ -55,10 +88,12 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
JSONObject data = new JSONObject(Utils.readFromConnection(conn));
if (!storiesig && !highlight) data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
if (!storiesig && !highlight)
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
else if (highlight) data = data.getJSONObject("reels").optJSONObject(id);
if (username == null && !isLoc && !isHashtag) username = data.getJSONObject("user").getString("username");
if (username == null && !isLoc && !isHashtag)
username = data.getJSONObject("user").getString("username");
JSONArray media;
if (data != null && (media = data.optJSONArray("items")) != null

View File

@ -0,0 +1,71 @@
package awais.instagrabber.customviews;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
public class PrimaryActionModeCallback implements ActionMode.Callback {
private ActionMode mode;
private int menuRes;
private final Callbacks callbacks;
public PrimaryActionModeCallback(final int menuRes, final Callbacks callbacks) {
this.menuRes = menuRes;
this.callbacks = callbacks;
}
@Override
public boolean onCreateActionMode(final ActionMode mode, final Menu menu) {
this.mode = mode;
mode.getMenuInflater().inflate(menuRes, menu);
if (callbacks != null) {
callbacks.onCreate(mode, menu);
}
return true;
}
@Override
public boolean onPrepareActionMode(final ActionMode mode, final Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (callbacks != null) {
return callbacks.onActionItemClicked(mode, item);
}
return false;
}
@Override
public void onDestroyActionMode(final ActionMode mode) {
if (callbacks != null) {
callbacks.onDestroy(mode);
}
this.mode = null;
}
public abstract static class CallbacksHelper implements Callbacks {
public void onCreate(final ActionMode mode, final Menu menu) {
}
@Override
public void onDestroy(final ActionMode mode) {
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
return false;
}
}
public interface Callbacks {
void onCreate(final ActionMode mode, final Menu menu);
void onDestroy(final ActionMode mode);
boolean onActionItemClicked(final ActionMode mode, final MenuItem item);
}
}

View File

@ -33,6 +33,8 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyInflater;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.DraweeView;
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
/**
* DraweeView that has zoomable capabilities.

View File

@ -0,0 +1,33 @@
package awais.instagrabber.customviews.helpers;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class CustomHideBottomViewOnScrollBehavior extends HideBottomViewOnScrollBehavior<BottomNavigationView> {
private static final String TAG = "CustomHideBottomView";
@Override
public boolean onStartNestedScroll(@NonNull final CoordinatorLayout coordinatorLayout,
@NonNull final BottomNavigationView child,
@NonNull final View directTargetChild,
@NonNull final View target,
final int nestedScrollAxes,
final int type) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
@Override
public void onNestedPreScroll(@NonNull final CoordinatorLayout coordinatorLayout, @NonNull final BottomNavigationView child, @NonNull final View target, final int dx, final int dy, @NonNull final int[] consumed, final int type) {
if (dy > 0) {
slideDown(child);
} else if (dy < 0) {
slideUp(child);
}
}
}

View File

@ -0,0 +1,154 @@
package awais.instagrabber.customviews.helpers;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.NestedScrollingChild;
import androidx.core.view.NestedScrollingChildHelper;
public class NestedCoordinatorLayout extends CoordinatorLayout implements NestedScrollingChild {
private NestedScrollingChildHelper mChildHelper;
public NestedCoordinatorLayout(Context context) {
super(context);
mChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
public NestedCoordinatorLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
public NestedCoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int type) {
int[][] tConsumed = new int[2][2];
super.onNestedPreScroll(target, dx, dy, consumed, type);
dispatchNestedPreScroll(dx, dy, tConsumed[1], null);
consumed[0] = tConsumed[0][0] + tConsumed[1][0];
consumed[1] = tConsumed[0][1] + tConsumed[1][1];
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null);
}
@Override
public void onStopNestedScroll(View target, int type) {
/* Disable the scrolling behavior of our own children */
super.onStopNestedScroll(target, type);
/* Disable the scrolling behavior of the parent's other children */
stopNestedScroll();
}
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes, int type) {
/* Enable the scrolling behavior of our own children */
boolean tHandled = super.onStartNestedScroll(child, target, nestedScrollAxes, type);
/* Enable the scrolling behavior of the parent's other children */
return startNestedScroll(nestedScrollAxes) || tHandled;
}
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
/* Enable the scrolling behavior of our own children */
boolean tHandled = super.onStartNestedScroll(child, target, nestedScrollAxes);
/* Enable the scrolling behavior of the parent's other children */
return startNestedScroll(nestedScrollAxes) || tHandled;
}
@Override
public void onStopNestedScroll(View target) {
/* Disable the scrolling behavior of our own children */
super.onStopNestedScroll(target);
/* Disable the scrolling behavior of the parent's other children */
stopNestedScroll();
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
int[][] tConsumed = new int[2][2];
super.onNestedPreScroll(target, dx, dy, tConsumed[0]);
dispatchNestedPreScroll(dx, dy, tConsumed[1], null);
consumed[0] = tConsumed[0][0] + tConsumed[1][0];
consumed[1] = tConsumed[0][1] + tConsumed[1][1];
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null);
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
boolean tHandled = super.onNestedPreFling(target, velocityX, velocityY);
return dispatchNestedPreFling(velocityX, velocityY) || tHandled;
}
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
boolean tHandled = super.onNestedFling(target, velocityX, velocityY, consumed);
return dispatchNestedFling(velocityX, velocityY, consumed) || tHandled;
}
@Override
public boolean isNestedScrollingEnabled() {
return mChildHelper.isNestedScrollingEnabled();
}
@Override
public void setNestedScrollingEnabled(boolean enabled) {
mChildHelper.setNestedScrollingEnabled(enabled);
}
@Override
public boolean startNestedScroll(int axes) {
return mChildHelper.startNestedScroll(axes);
}
@Override
public void stopNestedScroll() {
mChildHelper.stopNestedScroll();
}
@Override
public boolean hasNestedScrollingParent() {
return mChildHelper.hasNestedScrollingParent();
}
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed,
dyUnconsumed, offsetInWindow);
}
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
}

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.activities.MainActivityBackup;
import awais.instagrabber.adapters.SimpleAdapter;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
@ -112,8 +113,8 @@ public final class QuickAccessDialog extends BottomSheetDialogFragment implement
else Utils.showImportExportDialog(v.getContext());
} else if (tag instanceof DataBox.FavoriteModel) {
if (MainActivity.scanHack != null) {
MainActivity.scanHack.onResult(((DataBox.FavoriteModel) tag).getQuery());
if (MainActivityBackup.scanHack != null) {
MainActivityBackup.scanHack.onResult(((DataBox.FavoriteModel) tag).getQuery());
dismiss();
}

View File

@ -62,24 +62,24 @@ public final class MultiDirectDialog extends BaseLanguageActivity {
postModel.getSliderDisplayUrl(), postModel.getShortCode(), postModel.getPostCaption(), postModel.getTimestamp(),
postModel.getLike(), postModel.getBookmark(), postModel.getLikes()));
postsAdapter = new PostsAdapter(models, v -> {
final Object tag = v.getTag();
if (tag instanceof PostModel) {
final PostModel postModel = (PostModel) tag;
if (postsAdapter.isSelecting) toggleSelection(postModel);
else {
Utils.batchDownload(this, username, DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(postModel));
finish();
}
}
}, v -> {
final Object tag = v.getTag();
if (tag instanceof PostModel) {
postsAdapter.isSelecting = true;
toggleSelection((PostModel) tag);
}
return true;
});
// postsAdapter = new PostsAdapter(models, v -> {
// final Object tag = v.getTag();
// if (tag instanceof PostModel) {
// final PostModel postModel = (PostModel) tag;
// if (postsAdapter.isSelecting) toggleSelection(postModel);
// else {
// Utils.batchDownload(this, username, DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(postModel));
// finish();
// }
// }
// }, v -> {
// final Object tag = v.getTag();
// if (tag instanceof PostModel) {
// postsAdapter.isSelecting = true;
// toggleSelection((PostModel) tag);
// }
// return true;
// });
recyclerView.setAdapter(postsAdapter);
}
@ -109,10 +109,10 @@ public final class MultiDirectDialog extends BaseLanguageActivity {
}
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 (btnDownload != null) btnDownload.setVisible(postsAdapter.isSelecting);
// if (selectedItems.size() < 1) postsAdapter.isSelecting = false;
// if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged();
// else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel);
//
// if (btnDownload != null) btnDownload.setVisible(postsAdapter.isSelecting);
}
}

View File

@ -0,0 +1,673 @@
package awais.instagrabber.fragments;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Animatable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.util.Pair;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GestureDetectorCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.adapters.StoriesAdapter;
import awais.instagrabber.asyncs.CommentAction;
import awais.instagrabber.asyncs.DownloadAsync;
import awais.instagrabber.asyncs.QuizAction;
import awais.instagrabber.asyncs.RespondAction;
import awais.instagrabber.asyncs.SeenAction;
import awais.instagrabber.asyncs.VoteAction;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
import awais.instagrabber.customviews.helpers.SwipeGestureListener;
import awais.instagrabber.databinding.ActivityStoryViewerBinding;
import awais.instagrabber.fragments.main.viewmodels.FeedStoriesViewModel;
import awais.instagrabber.fragments.main.viewmodels.StoriesViewModel;
import awais.instagrabber.interfaces.SwipeEvent;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.services.ServiceCallback;
import awais.instagrabber.services.StoriesService;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD;
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class StoryViewerFragment extends Fragment {
private static final String TAG = "StoryViewerFragment";
private AppCompatActivity fragmentActivity;
private View root;
private ActivityStoryViewerBinding binding;
private String currentStoryUsername;
private StoriesAdapter storiesAdapter;
private SwipeEvent swipeEvent;
private GestureDetectorCompat gestureDetector;
private StoriesService storiesService;
private List<FeedStoryModel> feedStoryModels;
private StoryModel currentStory;
private int slidePos;
private int lastSlidePos;
private String url;
private PollModel poll;
private QuestionModel question;
private String[] mentions;
private QuizModel quiz;
private MenuItem menuDownload;
private MenuItem menuDm;
private SimpleExoPlayer player;
private boolean isHashtag;
private String highlight;
private boolean fetching = false;
private int currentFeedStoryIndex;
private StoriesViewModel storiesViewModel;
private String currentStoryMediaId;
private final String cookie = settingsHelper.getString(Constants.COOKIE);
private StoryViewerFragmentArgs fragmentArgs;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (AppCompatActivity) requireActivity();
storiesService = StoriesService.getInstance();
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
if (root != null) {
return root;
}
binding = ActivityStoryViewerBinding.inflate(inflater, container, false);
root = binding.getRoot();
return root;
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
init();
}
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu, final MenuInflater menuInflater) {
menuInflater.inflate(R.menu.story_menu, menu);
menuDownload = menu.findItem(R.id.action_download);
menuDm = menu.findItem(R.id.action_dms);
menuDownload.setVisible(false);
menuDm.setVisible(false);
}
@Override
public void onPrepareOptionsMenu(@NonNull final Menu menu) {
// hide menu items from activity
final MenuItem item = menu.findItem(R.id.favourites);
if (item != null) {
item.setVisible(false);
}
}
@Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_download:
if (ContextCompat.checkSelfPermission(requireContext(), Utils.PERMS[0]) == PackageManager.PERMISSION_GRANTED)
downloadStory();
else
ActivityCompat.requestPermissions(requireActivity(), Utils.PERMS, 8020);
return true;
case R.id.action_dms:
final EditText input = new EditText(requireContext());
input.setHint(R.string.reply_hint);
new AlertDialog.Builder(requireContext())
.setTitle(R.string.reply_story)
.setView(input)
.setPositiveButton(R.string.ok, (d, w) -> new CommentAction(cookie, currentStory, threadId -> {
try {
final DirectThreadBroadcaster.StoryReplyBroadcastOptions options = new DirectThreadBroadcaster.StoryReplyBroadcastOptions(
input.getText().toString(),
currentStory.getStoryMediaId(),
currentStory.getUserId()
);
final DirectThreadBroadcaster broadcast = new DirectThreadBroadcaster(threadId);
broadcast.setOnTaskCompleteListener(result -> Toast.makeText(
requireContext(),
result != null ? R.string.answered_story : R.string.downloader_unknown_error,
Toast.LENGTH_SHORT
).show());
broadcast.execute(options);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Error", e);
}
}).execute())
.setNegativeButton(R.string.cancel, null)
.show();
return true;
}
return false;
}
@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)
downloadStory();
}
@Override
public void onPause() {
super.onPause();
releasePlayer();
}
@Override
public void onDestroy() {
releasePlayer();
// reset subtitle
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setSubtitle(null);
}
super.onDestroy();
}
private void init() {
final FeedStoriesViewModel feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
feedStoryModels = feedStoriesViewModel.getList().getValue();
if (feedStoryModels == null || feedStoryModels.isEmpty() || getArguments() == null) return;
fragmentArgs = StoryViewerFragmentArgs.fromBundle(getArguments());
currentFeedStoryIndex = fragmentArgs.getFeedStoryIndex();
setupStories();
}
private void setupStories() {
storiesViewModel = new ViewModelProvider(this).get(StoriesViewModel.class);
setupListeners();
binding.storiesList.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
storiesAdapter = new StoriesAdapter((model, position) -> {
currentStory = model;
slidePos = position;
refreshStory();
});
binding.storiesList.setAdapter(storiesAdapter);
storiesViewModel.getList().observe(fragmentActivity, storiesAdapter::submitList);
resetView();
}
@SuppressLint("ClickableViewAccessibility")
private void setupListeners() {
swipeEvent = isRightSwipe -> {
final List<StoryModel> storyModels = storiesViewModel.getList().getValue();
final int storiesLen = storyModels == null ? 0 : storyModels.size();
if (storiesLen <= 0) return;
final boolean hasFeedStories = feedStoryModels != null && !feedStoryModels.isEmpty();
final boolean isLeftSwipe = !isRightSwipe;
final boolean endOfCurrentStories = slidePos + 1 >= storiesLen;
final boolean swipingBeyondCurrentStories = (endOfCurrentStories && isLeftSwipe) || (slidePos == 0 && isRightSwipe);
if (swipingBeyondCurrentStories && hasFeedStories) {
final int index = currentFeedStoryIndex;
if (settingsHelper.getBoolean(MARK_AS_SEEN)) {
new SeenAction(cookie, currentStory).execute();
}
if ((isRightSwipe && index == 0) || (isLeftSwipe && index == feedStoryModels.size() - 1)) {
Toast.makeText(requireContext(), R.string.no_more_stories, Toast.LENGTH_SHORT).show();
return;
}
final FeedStoryModel feedStoryModel = isRightSwipe
? feedStoryModels.get(index - 1)
: feedStoryModels.size() == index + 1 ? null : feedStoryModels.get(index + 1);
if (feedStoryModel != null) {
if (fetching) {
Toast.makeText(requireContext(), R.string.be_patient, Toast.LENGTH_SHORT).show();
return;
}
fetching = true;
currentFeedStoryIndex = isRightSwipe ? (index - 1) : (index + 1);
resetView();
// new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> {
// if (result != null && result.length > 0) {
// final Intent newIntent = new Intent(requireContext(), StoryViewer.class)
// .putExtra(Constants.EXTRAS_STORIES, result)
// .putExtra(Constants.EXTRAS_USERNAME, feedStoryModel.getProfileModel().getUsername())
// .putExtra(Constants.FEED, storyFeed)
// .putExtra(Constants.FEED_ORDER, isRightSwipe ? (index - 1) : (index + 1));
// newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// startActivity(newIntent);
// } else
// Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
// }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
return;
}
if (isRightSwipe) {
if (--slidePos <= 0) {
slidePos = 0;
}
} else if (++slidePos >= storiesLen) {
slidePos = storiesLen - 1;
}
currentStory = storyModels.get(slidePos);
refreshStory();
};
gestureDetector = new GestureDetectorCompat(requireContext(), new SwipeGestureListener(swipeEvent));
binding.playerView.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
final GestureDetector.SimpleOnGestureListener simpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) {
final float diffX = e2.getX() - e1.getX();
try {
if (Math.abs(diffX) > Math.abs(e2.getY() - e1.getY()) && Math.abs(diffX) > SWIPE_THRESHOLD
&& Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
swipeEvent.onSwipe(diffX > 0);
return true;
}
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ACTIVITY_STORY_VIEWER, "setupListeners",
new Pair<>("swipeEvent", swipeEvent),
new Pair<>("diffX", diffX));
if (BuildConfig.DEBUG) Log.e(TAG, "Error", e);
}
return false;
}
};
binding.imageViewer.setTapListener(simpleOnGestureListener);
binding.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);
}
});
binding.viewStoryPost.setOnClickListener(v -> {
final Object tag = v.getTag();
if (tag instanceof CharSequence)
startActivity(new Intent(requireContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_POST, new PostModel(tag.toString(), tag.toString().matches("^[\\d]+$"))));
});
final View.OnClickListener storyActionListener = v -> {
final Object tag = v.getTag();
if (tag instanceof PollModel) {
poll = (PollModel) tag;
if (poll.getMyChoice() > -1) {
new AlertDialog.Builder(requireContext()).setTitle(R.string.voted_story_poll)
.setAdapter(new ArrayAdapter<>(requireContext(), 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(requireContext())
.setTitle(poll.getQuestion())
.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, new String[]{
poll.getLeftChoice() + " (" + poll.getLeftCount() + ")",
poll.getRightChoice() + " (" + poll.getRightCount() + ")"
}), (d, w) -> {
if (!Utils.isEmpty(cookie))
new VoteAction(currentStory, poll, cookie, choice -> {
if (choice > -1) {
poll.setMyChoice(choice);
Toast.makeText(requireContext(), R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).execute(w);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
} else if (tag instanceof QuestionModel) {
question = (QuestionModel) tag;
final EditText input = new EditText(requireContext());
input.setHint(R.string.answer_hint);
new AlertDialog.Builder(requireContext())
.setTitle(question.getQuestion())
.setView(input)
.setPositiveButton(R.string.ok, (d, w) -> new RespondAction(currentStory, question, cookie, result -> {
if (result) {
Toast.makeText(requireContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
} else
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).execute(input.getText().toString()))
.setNegativeButton(R.string.cancel, null)
.show();
} else if (tag instanceof String[]) {
mentions = (String[]) tag;
new AlertDialog.Builder(requireContext())
.setTitle(R.string.story_mentions)
.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, mentions), (d, w) -> {
// searchUsername(mentions[w]);
})
.setPositiveButton(R.string.cancel, null)
.show();
} else if (tag instanceof QuizModel) {
String[] choices = new String[quiz.getChoices().length];
for (int q = 0; q < choices.length; ++q) {
choices[q] = (quiz.getMyChoice() == q ? "" : "") + quiz.getChoices()[q] + " (" + quiz.getCounts()[q] + ")";
}
new AlertDialog.Builder(requireContext())
.setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion())
.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, choices), (d, w) -> {
if (quiz.getMyChoice() == -1 && !Utils.isEmpty(cookie))
new QuizAction(currentStory, quiz, cookie, choice -> {
if (choice > -1) {
quiz.setMyChoice(choice);
Toast.makeText(requireContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).execute(w);
})
.setPositiveButton(R.string.cancel, null)
.show();
}
};
binding.poll.setOnClickListener(storyActionListener);
binding.answer.setOnClickListener(storyActionListener);
binding.mention.setOnClickListener(storyActionListener);
binding.quiz.setOnClickListener(storyActionListener);
}
private void resetView() {
slidePos = 0;
lastSlidePos = 0;
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
binding.imageViewer.setController(null);
releasePlayer();
final FeedStoryModel feedStoryModel = feedStoryModels.get(currentFeedStoryIndex);
currentStoryMediaId = feedStoryModel.getStoryMediaId();
currentStoryUsername = feedStoryModel.getProfileModel().getUsername();
isHashtag = fragmentArgs.getIsHashtag();
highlight = fragmentArgs.getHighlight();
final boolean hasUsername = !Utils.isEmpty(currentStoryUsername);
final boolean hasHighlight = !Utils.isEmpty(highlight);
if (hasUsername) {
currentStoryUsername = currentStoryUsername.replace("@", "");
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(currentStoryUsername);
// actionBar.setOnClickListener(v -> {
// searchUsername(username);
// });
if (hasHighlight) {
actionBar.setSubtitle(getString(R.string.title_highlight, highlight));
} else {
actionBar.setSubtitle(R.string.title_user_story);
}
}
}
storiesViewModel.getList().setValue(Collections.emptyList());
storiesService.getUserStory(currentStoryMediaId, null, false, false, false, false, new ServiceCallback<List<StoryModel>>() {
@Override
public void onSuccess(final List<StoryModel> storyModels) {
fetching = false;
if (storyModels == null || storyModels.isEmpty()) {
storiesViewModel.getList().setValue(Collections.emptyList());
currentStory = null;
binding.storiesList.setVisibility(View.GONE);
return;
}
binding.storiesList.setVisibility(View.VISIBLE);
storiesViewModel.getList().setValue(storyModels);
currentStory = storyModels.get(0);
refreshStory();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error", t);
}
});
}
private void refreshStory() {
if (binding.storiesList.getVisibility() == View.VISIBLE) {
final List<StoryModel> storyModels = storiesViewModel.getList().getValue();
if (storyModels != null) {
StoryModel item = storyModels.get(lastSlidePos);
if (item != null) {
item.setCurrentSlide(false);
storiesAdapter.notifyItemChanged(lastSlidePos, item);
}
item = storyModels.get(slidePos);
if (item != null) {
item.setCurrentSlide(true);
storiesAdapter.notifyItemChanged(slidePos, item);
}
}
}
lastSlidePos = slidePos;
final MediaItemType itemType = currentStory.getItemType();
if (menuDownload != null) menuDownload.setVisible(false);
url = itemType == MediaItemType.MEDIA_TYPE_VIDEO ? currentStory.getVideoUrl() : currentStory.getStoryUrl();
final String shortCode = currentStory.getTappableShortCode();
binding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE);
binding.viewStoryPost.setTag(shortCode);
final String spotify = currentStory.getSpotify();
binding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE);
binding.spotify.setTag(spotify);
poll = currentStory.getPoll();
binding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE);
binding.poll.setTag(poll);
question = currentStory.getQuestion();
binding.answer.setVisibility((question != null && !Utils.isEmpty(cookie)) ? View.VISIBLE : View.GONE);
binding.answer.setTag(question);
mentions = currentStory.getMentions();
binding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE);
binding.mention.setTag(mentions);
quiz = currentStory.getQuiz();
binding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE);
binding.quiz.setTag(quiz);
releasePlayer();
if (isHashtag) {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(currentStory.getUsername() + " (" + currentStoryUsername + ")");
}
// binding.toolbar.toolbar.setOnClickListener(v -> {
// searchUsername(currentStory.getUsername());
// });
}
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo();
else setupImage();
if (Utils.isEmpty(highlight)) {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L)));
}
}
if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction(cookie, currentStory).execute();
}
private void downloadStory() {
int error = 0;
if (currentStory != null) {
File dir = new File(Environment.getExternalStorageDirectory(), "Download");
if (settingsHelper.getBoolean(FOLDER_SAVE_TO)) {
final String customPath = settingsHelper.getString(FOLDER_PATH);
if (!Utils.isEmpty(customPath)) dir = new File(customPath);
}
if (settingsHelper.getBoolean(Constants.DOWNLOAD_USER_FOLDER) && !Utils.isEmpty(currentStoryUsername))
dir = new File(dir, currentStoryUsername);
if (dir.exists() || dir.mkdirs()) {
final String storyUrl = currentStory.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO ? currentStory.getVideoUrl() : currentStory.getStoryUrl();
final File saveFile = new File(dir, currentStory.getStoryMediaId() + "_" + currentStory.getTimestamp()
+ Utils.getExtensionFromModel(storyUrl, currentStory));
new DownloadAsync(requireContext(), storyUrl, saveFile, result -> {
final int toastRes = result != null && result.exists() ? R.string.downloader_complete
: R.string.downloader_error_download_file;
Toast.makeText(requireContext(), toastRes, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else error = 1;
} else error = 2;
if (error == 1)
Toast.makeText(requireContext(), R.string.downloader_error_creating_folder, Toast.LENGTH_SHORT).show();
else if (error == 2)
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
private void setupImage() {
binding.progressView.setVisibility(View.VISIBLE);
binding.playerView.setVisibility(View.GONE);
binding.imageViewer.setVisibility(View.VISIBLE);
final ImageRequest requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url))
.setLocalThumbnailPreviewsEnabled(true)
.setProgressiveRenderingEnabled(true)
.build();
final DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(requestBuilder)
.setOldController(binding.imageViewer.getController())
.setControllerListener(new BaseControllerListener<ImageInfo>() {
@Override
public void onFinalImageSet(final String id, final ImageInfo imageInfo, final Animatable animatable) {
if (menuDownload != null) {
menuDownload.setVisible(true);
}
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) {
menuDm.setVisible(true);
}
binding.progressView.setVisibility(View.GONE);
}
})
.build();
binding.imageViewer.setController(controller);
}
private void setupVideo() {
binding.playerView.setVisibility(View.VISIBLE);
binding.progressView.setVisibility(View.GONE);
binding.imageViewer.setVisibility(View.GONE);
binding.imageViewer.setController(null);
player = new SimpleExoPlayer.Builder(requireContext()).build();
binding.playerView.setPlayer(player);
player.setPlayWhenReady(settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS));
final ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(requireContext(), "instagram"))
.createMediaSource(Uri.parse(url));
mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() {
@Override
public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie))
menuDm.setVisible(true);
binding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie))
menuDm.setVisible(true);
binding.progressView.setVisibility(View.VISIBLE);
}
@Override
public void onLoadCanceled(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
binding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadError(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData, final IOException error, final boolean wasCanceled) {
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
binding.progressView.setVisibility(View.GONE);
}
});
player.prepare(mediaSource);
binding.playerView.setOnClickListener(v -> {
if (player != null) {
if (player.getPlaybackState() == Player.STATE_ENDED) player.seekTo(0);
player.setPlayWhenReady(player.getPlaybackState() == Player.STATE_ENDED || !player.isPlaying());
}
});
}
private void releasePlayer() {
if (player == null) return;
try { player.stop(true); } catch (Exception ignored) { }
try { player.release(); } catch (Exception ignored) { }
player = null;
}
}

View File

@ -28,6 +28,7 @@ import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.adapters.DirectMessageInboxAdapter;
import awais.instagrabber.asyncs.direct_messages.InboxFetcher;
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentDirectMessagesInboxBinding;
import awais.instagrabber.interfaces.FetchListener;
@ -39,7 +40,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
private static final String TAG = "DirectMessagesInboxFrag";
private FragmentActivity fragmentActivity;
private SwipeRefreshLayout root;
private NestedCoordinatorLayout root;
private RecyclerView inboxList;
private RecyclerLazyLoader lazyLoader;
private LinearLayoutManager layoutManager;
@ -51,7 +52,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
private final FetchListener<InboxModel> fetchListener = new FetchListener<InboxModel>() {
@Override
public void doBefore() {
root.setRefreshing(true);
binding.swipeRefreshLayout.setRefreshing(true);
}
@Override
@ -71,10 +72,11 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
listViewModel.getList().postValue(list);
}
}
root.setRefreshing(false);
binding.swipeRefreshLayout.setRefreshing(false);
stopCurrentExecutor();
}
};
private FragmentDirectMessagesInboxBinding binding;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@ -89,9 +91,9 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
if (root != null) {
return root;
}
final FragmentDirectMessagesInboxBinding binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false);
binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false);
root = binding.getRoot();
root.setOnRefreshListener(this);
binding.swipeRefreshLayout.setOnRefreshListener(this);
inboxList = binding.inboxList;
inboxList.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(requireContext());

View File

@ -50,7 +50,6 @@ import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.activities.ProfileViewer;
import awais.instagrabber.activities.StoryViewer;
import awais.instagrabber.adapters.DirectMessageItemsAdapter;
import awais.instagrabber.asyncs.ImageUploader;
import awais.instagrabber.asyncs.direct_messages.DirectMessageInboxThreadFetcher;
@ -225,21 +224,21 @@ public class DirectMessageThreadFragment extends Fragment {
break;
case STORY_SHARE:
if (directItemModel.getReelShare() != null) {
StoryModel sm = new StoryModel(
directItemModel.getReelShare().getReelId(),
directItemModel.getReelShare().getMedia().getVideoUrl(),
directItemModel.getReelShare().getMedia().getMediaType(),
directItemModel.getTimestamp(),
directItemModel.getReelShare().getReelOwnerName(),
String.valueOf(directItemModel.getReelShare().getReelOwnerId()),
false
);
sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl());
StoryModel[] sms = {sm};
startActivity(new Intent(requireContext(), StoryViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName())
.putExtra(Constants.EXTRAS_STORIES, sms)
);
// StoryModel sm = new StoryModel(
// directItemModel.getReelShare().getReelId(),
// directItemModel.getReelShare().getMedia().getVideoUrl(),
// directItemModel.getReelShare().getMedia().getMediaType(),
// directItemModel.getTimestamp(),
// directItemModel.getReelShare().getReelOwnerName(),
// String.valueOf(directItemModel.getReelShare().getReelOwnerId()),
// false
// );
// sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl());
// StoryModel[] sms = {sm};
// startActivity(new Intent(requireContext(), StoryViewer.class)
// .putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName())
// .putExtra(Constants.EXTRAS_STORIES, sms)
// );
} else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) {
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]);
}

View File

@ -0,0 +1,241 @@
package awais.instagrabber.fragments.main;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.adapters.DiscoverAdapter;
import awais.instagrabber.asyncs.DiscoverFetcher;
import awais.instagrabber.asyncs.i.iTopicFetcher;
import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentDiscoverBinding;
import awais.instagrabber.fragments.main.viewmodels.DiscoverItemViewModel;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.DiscoverItemModel;
import awais.instagrabber.models.DiscoverTopicModel;
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;
public class DiscoverFragment extends Fragment {
private MainActivity fragmentActivity;
private CoordinatorLayout root;
private FragmentDiscoverBinding binding;
private DiscoverAdapter discoverAdapter;
private RecyclerLazyLoader lazyLoader;
private boolean discoverHasMore = false;
private String[] topicIds;
private String rankToken;
private String currentTopic;
private String discoverEndMaxId;
private ActionMode actionMode;
private DiscoverItemViewModel discoverItemViewModel;
private final FetchListener<DiscoverTopicModel> topicFetchListener = new FetchListener<DiscoverTopicModel>() {
@Override
public void doBefore() {}
@Override
public void onResult(final DiscoverTopicModel result) {
if (result != null) {
topicIds = result.getIds();
rankToken = result.getToken();
final ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(
requireContext(),
android.R.layout.simple_spinner_dropdown_item,
result.getNames()
);
binding.discoverType.setAdapter(spinnerArrayAdapter);
}
}
};
private final FetchListener<DiscoverItemModel[]> discoverFetchListener = new FetchListener<DiscoverItemModel[]>() {
@Override
public void doBefore() {
binding.discoverSwipeRefreshLayout.setRefreshing(true);
}
@Override
public void onResult(final DiscoverItemModel[] result) {
binding.discoverSwipeRefreshLayout.setRefreshing(false);
if (result == null || result.length == 0) {
Toast.makeText(requireContext(), R.string.discover_empty, Toast.LENGTH_SHORT).show();
return;
}
final List<DiscoverItemModel> current = discoverItemViewModel.getList().getValue();
final List<DiscoverItemModel> resultList = Arrays.asList(result);
if (current == null) {
discoverItemViewModel.getList().postValue(resultList);
} else {
final List<DiscoverItemModel> currentCopy = new ArrayList<>(current);
currentCopy.addAll(resultList);
discoverItemViewModel.getList().postValue(currentCopy);
}
final DiscoverItemModel discoverItemModel = result[result.length - 1];
if (discoverItemModel != null) {
discoverEndMaxId = discoverItemModel.getNextMaxId();
discoverHasMore = discoverItemModel.hasMore();
discoverItemModel.setMore(false, null);
}
}
};
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (discoverAdapter == null) {
remove();
return;
}
discoverAdapter.clearSelection();
remove();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
R.menu.multi_select_download_menu,
new PrimaryActionModeCallback.CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
onBackPressedCallback.handleOnBackPressed();
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
if (discoverAdapter == null) return false;
Utils.batchDownload(requireContext(),
null,
DownloadMethod.DOWNLOAD_DISCOVER,
discoverAdapter.getSelectedModels());
checkAndResetAction();
return true;
}
return false;
}
});
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity();
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
final ViewGroup container,
final Bundle savedInstanceState) {
if (root != null) {
return root;
}
binding = FragmentDiscoverBinding.inflate(inflater, container, false);
root = binding.getRoot();
setupExplore();
return root;
}
private void setupExplore() {
discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class);
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(requireContext(), Utils.convertDpToPx(110));
binding.discoverPosts.setLayoutManager(layoutManager);
binding.discoverPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
new iTopicFetcher(topicFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
binding.discoverType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (topicIds != null) {
currentTopic = topicIds[pos];
binding.discoverSwipeRefreshLayout.setRefreshing(true);
if (lazyLoader != null) lazyLoader.resetState();
discoverItemViewModel.getList().postValue(Collections.emptyList());
new DiscoverFetcher(currentTopic, null, rankToken, discoverFetchListener, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});
binding.discoverSwipeRefreshLayout.setOnRefreshListener(() -> {
lazyLoader.resetState();
discoverItemViewModel.getList().postValue(Collections.emptyList());
new DiscoverFetcher(currentTopic, null, rankToken, discoverFetchListener, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
discoverAdapter = new DiscoverAdapter((model, position) -> {
if (discoverAdapter.isSelecting()) {
if (actionMode == null) return;
final String title = getString(R.string.number_selected, discoverAdapter.getSelectedModels().size());
actionMode.setTitle(title);
return;
}
if (checkAndResetAction()) return;
startActivity(new Intent(requireContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, position)
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.DISCOVER_ITEMS)
.putExtra(Constants.EXTRAS_POST, new PostModel(model.getShortCode(), false)));
}, (model, position) -> {
if (!discoverAdapter.isSelecting()) {
checkAndResetAction();
return true;
}
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
if (onBackPressedDispatcher.hasEnabledCallbacks()) {
return true;
}
actionMode = fragmentActivity.startActionMode(multiSelectAction);
final String title = getString(R.string.number_selected, 1);
actionMode.setTitle(title);
onBackPressedDispatcher.addCallback(onBackPressedCallback);
return true;
});
binding.discoverPosts.setAdapter(discoverAdapter);
discoverItemViewModel.getList().observe(fragmentActivity, discoverAdapter::submitList);
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if (discoverHasMore) {
binding.discoverSwipeRefreshLayout.setRefreshing(true);
new DiscoverFetcher(currentTopic, discoverEndMaxId, rankToken, discoverFetchListener, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
discoverEndMaxId = null;
}
});
binding.discoverPosts.addOnScrollListener(lazyLoader);
}
private boolean checkAndResetAction() {
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
if (!onBackPressedDispatcher.hasEnabledCallbacks() || actionMode == null) {
return false;
}
actionMode.finish();
actionMode = null;
return true;
}
}

View File

@ -0,0 +1,345 @@
package awais.instagrabber.fragments.main;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.common.executors.UiThreadImmediateExecutorService;
import com.facebook.datasource.BaseDataSubscriber;
import com.facebook.datasource.DataSource;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.request.ImageRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import awais.instagrabber.R;
import awais.instagrabber.activities.CommentsViewer;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.adapters.FeedAdapter;
import awais.instagrabber.adapters.FeedStoriesAdapter;
import awais.instagrabber.adapters.viewholder.feed.FeedItemViewHolder;
import awais.instagrabber.asyncs.FeedFetcher;
import awais.instagrabber.customviews.RamboTextView;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.customviews.helpers.VideoAwareRecyclerScroller;
import awais.instagrabber.databinding.FragmentFeedBinding;
import awais.instagrabber.fragments.main.viewmodels.FeedStoriesViewModel;
import awais.instagrabber.fragments.main.viewmodels.FeedViewModel;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.BasePostModel;
import awais.instagrabber.models.FeedModel;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.ViewerPostModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.services.ServiceCallback;
import awais.instagrabber.services.StoriesService;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class FeedFragment extends Fragment {
private static final String TAG = "FeedFragment";
private static final double MAX_VIDEO_HEIGHT = 0.9 * Utils.displayMetrics.heightPixels;
private static final int RESIZED_VIDEO_HEIGHT = (int) (0.8 * Utils.displayMetrics.heightPixels);
public static final boolean SHOULD_AUTO_PLAY = settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS);
private MainActivity fragmentActivity;
private CoordinatorLayout root;
private FragmentFeedBinding binding;
private StoriesService storiesService;
private boolean feedHasNextPage = false;
private String feedEndCursor = null;
private FeedViewModel feedViewModel;
private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
private final FetchListener<FeedModel[]> feedFetchListener = new FetchListener<FeedModel[]>() {
@Override
public void doBefore() {
binding.feedSwipeRefreshLayout.post(() -> binding.feedSwipeRefreshLayout.setRefreshing(true));
}
@Override
public void onResult(final FeedModel[] result) {
if (result == null) return;
final List<FeedModel> currentFeedModelList = feedViewModel.getList().getValue();
final Map<String, FeedModel> thumbToFeedMap = new HashMap<>();
for (final FeedModel feedModel : result) {
thumbToFeedMap.put(feedModel.getThumbnailUrl(), feedModel);
}
final BaseDataSubscriber<Void> subscriber = new BaseDataSubscriber<Void>() {
int success = 0;
int failed = 0;
@Override
protected void onNewResultImpl(@NonNull final DataSource<Void> dataSource) {
final Map<String, Object> extras = dataSource.getExtras();
if (extras == null) return;
final Uri thumbUri = (Uri) extras.get("uri_source");
if (thumbUri == null) return;
final Integer encodedWidth = (Integer) extras.get("encoded_width");
final Integer encodedHeight = (Integer) extras.get("encoded_height");
if (encodedWidth == null || encodedHeight == null) return;
final FeedModel feedModel = thumbToFeedMap.get(thumbUri.toString());
if (feedModel == null) return;
int requiredWidth = Utils.displayMetrics.widthPixels;
int resultingHeight = Utils.getResultingHeight(requiredWidth, encodedHeight, encodedWidth);
if (feedModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO && resultingHeight >= MAX_VIDEO_HEIGHT) {
// If its a video and the height is too large, need to reduce the height,
// so that entire video fits on screen
resultingHeight = RESIZED_VIDEO_HEIGHT;
requiredWidth = Utils.getResultingWidth(RESIZED_VIDEO_HEIGHT, resultingHeight, requiredWidth);
}
feedModel.setImageWidth(requiredWidth);
feedModel.setImageHeight(resultingHeight);
success++;
updateAdapter();
}
@Override
protected void onFailureImpl(@NonNull final DataSource<Void> dataSource) {
failed++;
updateAdapter();
}
public void updateAdapter() {
if (failed + success != result.length) return;
final List<FeedModel> finalList = currentFeedModelList == null || currentFeedModelList.isEmpty() ? new ArrayList<>() : new ArrayList<>(currentFeedModelList);
finalList.addAll(Arrays.asList(result));
feedViewModel.getList().postValue(finalList);
final PostModel feedPostModel = result[result.length - 1];
if (feedPostModel != null) {
feedEndCursor = feedPostModel.getEndCursor();
feedHasNextPage = feedPostModel.hasNextPage();
feedPostModel.setPageCursor(false, null);
}
binding.feedSwipeRefreshLayout.setRefreshing(false);
}
};
for (final FeedModel feedModel : result) {
final DataSource<Void> ds = Fresco.getImagePipeline().prefetchToBitmapCache(ImageRequest.fromUri(feedModel.getThumbnailUrl()), null);
ds.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
}
}
};
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity();
storiesService = StoriesService.getInstance();
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
final ViewGroup container,
final Bundle savedInstanceState) {
if (root != null) {
return root;
}
binding = FragmentFeedBinding.inflate(inflater, container, false);
root = binding.getRoot();
// setupActionBar();
setupFeedStories();
setupFeed();
return root;
}
@Override
public void onPause() {
super.onPause();
if (videoAwareRecyclerScroller != null) {
videoAwareRecyclerScroller.stopPlaying();
}
}
@Override
public void onResume() {
super.onResume();
if (videoAwareRecyclerScroller != null && SHOULD_AUTO_PLAY) {
videoAwareRecyclerScroller.startPlaying();
}
}
final MentionClickListener mentionClickListener = (view, text, isHashtag) -> {
// final AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
// .setMessage(isHashtag ? R.string.comment_view_mention_hash_search : R.string.comment_view_mention_user_search)
// .setTitle(text)
// .setNegativeButton(R.string.cancel, null)
// .setPositiveButton(R.string.ok, (dialog, which) -> {
// // if (MainActivityBackup.scanHack != null) {
// // mainActivity.mainBinding.drawerLayout.closeDrawers();
// // MainActivityBackup.scanHack.onResult(text);
// // }
// });
// builder.show();
if (isHashtag) {
// hashtag...
return;
}
final NavDirections action = FeedFragmentDirections.actionFeedFragmentToProfileFragment("@" + text);
NavHostFragment.findNavController(this).navigate(action);
};
private final View.OnClickListener postViewClickListener = v -> {
final Object tag = v.getTag();
if (!(tag instanceof FeedModel)) return;
final FeedModel feedModel = (FeedModel) tag;
if (v instanceof RamboTextView) {
if (feedModel.isMentionClicked())
feedModel.toggleCaption();
feedModel.setMentionClicked(false);
if (!FeedItemViewHolder.expandCollapseTextView((RamboTextView) v, feedModel.getPostCaption()))
feedModel.toggleCaption();
return;
}
final int id = v.getId();
switch (id) {
case R.id.btnComments:
startActivity(new Intent(requireContext(), CommentsViewer.class)
.putExtra(Constants.EXTRAS_SHORTCODE, feedModel.getShortCode())
.putExtra(Constants.EXTRAS_POST, feedModel.getPostId())
.putExtra(Constants.EXTRAS_USER, feedModel.getProfileModel().getId()));
break;
case R.id.viewStoryPost:
startActivity(new Intent(requireContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, feedModel.getPosition())
.putExtra(Constants.EXTRAS_POST, new PostModel(feedModel.getShortCode(), false))
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.FEED_ITEMS));
break;
case R.id.btnDownload:
ProfileModel profileModel = feedModel.getProfileModel();
final String username = profileModel != null ? profileModel.getUsername() : null;
final ViewerPostModel[] sliderItems = feedModel.getSliderItems();
if (feedModel.getItemType() != MediaItemType.MEDIA_TYPE_SLIDER || sliderItems == null || sliderItems.length == 1)
Utils.batchDownload(requireContext(), username, DownloadMethod.DOWNLOAD_FEED, Collections.singletonList(feedModel));
else {
final ArrayList<BasePostModel> postModels = new ArrayList<>();
final DialogInterface.OnClickListener clickListener1 = (dialog, which) -> {
postModels.clear();
final boolean breakWhenFoundSelected = which == DialogInterface.BUTTON_POSITIVE;
for (final ViewerPostModel sliderItem : sliderItems) {
if (sliderItem != null) {
if (!breakWhenFoundSelected)
postModels.add(sliderItem);
else if (sliderItem.isSelected()) {
postModels.add(sliderItem);
break;
}
}
}
// shows 0 items on first item of viewpager cause onPageSelected hasn't been called yet
if (breakWhenFoundSelected && postModels.size() == 0)
postModels.add(sliderItems[0]);
if (postModels.size() > 0)
Utils.batchDownload(requireContext(), username, DownloadMethod.DOWNLOAD_FEED, postModels);
};
new AlertDialog.Builder(requireContext())
.setTitle(R.string.post_viewer_download_dialog_title)
.setPositiveButton(R.string.post_viewer_download_current, clickListener1)
.setNegativeButton(R.string.post_viewer_download_album, clickListener1)
.show();
}
break;
case R.id.ivProfilePic:
profileModel = feedModel.getProfileModel();
if (profileModel != null)
mentionClickListener.onClick(null, profileModel.getUsername(), false);
break;
}
};
private void setupFeed() {
feedViewModel = new ViewModelProvider(fragmentActivity).get(FeedViewModel.class);
final LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext());
binding.feedRecyclerView.setLayoutManager(layoutManager);
binding.feedRecyclerView.setHasFixedSize(true);
final FeedAdapter feedAdapter = new FeedAdapter(postViewClickListener, mentionClickListener);
feedAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
binding.feedRecyclerView.setAdapter(feedAdapter);
feedViewModel.getList().observe(fragmentActivity, feedAdapter::submitList);
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if (feedHasNextPage) {
fetchFeed();
}
});
if (SHOULD_AUTO_PLAY) {
videoAwareRecyclerScroller = new VideoAwareRecyclerScroller();
binding.feedRecyclerView.addOnScrollListener(videoAwareRecyclerScroller);
}
binding.feedRecyclerView.addOnScrollListener(lazyLoader);
fetchFeed();
}
private void fetchFeed() {
binding.feedSwipeRefreshLayout.setRefreshing(true);
new FeedFetcher(feedEndCursor, feedFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
feedEndCursor = null;
}
private void setupFeedStories() {
final FeedStoriesViewModel feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class);
final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter((model, position) -> {
final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(
position,
null,
false);
NavHostFragment.findNavController(this).navigate(action);
});
binding.feedStoriesRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false));
binding.feedStoriesRecyclerView.setAdapter(feedStoriesAdapter);
feedStoriesViewModel.getList().observe(fragmentActivity, feedStoriesAdapter::submitList);
storiesService.getFeedStories(new ServiceCallback<List<FeedStoryModel>>() {
@Override
public void onSuccess(final List<FeedStoryModel> result) {
feedStoriesViewModel.getList().postValue(result);
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "failed", t);
}
});
}
}

View File

@ -0,0 +1,57 @@
package awais.instagrabber.fragments.main;
import android.content.Intent;
import android.view.View;
import awais.instagrabber.MainHelper;
import awais.instagrabber.activities.SavedViewer;
import awais.instagrabber.databinding.FragmentProfileBinding;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.Utils;
public class ProfileActionListener implements View.OnClickListener {
private String cookie;
private boolean isLoggedIn;
private ProfileModel profileModel;
private String userQuery;
private FragmentProfileBinding binding;
private LocationModel locationModel;
public ProfileActionListener(final String cookie, final boolean isLoggedIn, final ProfileModel profileModel, final String userQuery, final FragmentProfileBinding binding, final LocationModel locationModel) {
this.cookie = cookie;
this.isLoggedIn = isLoggedIn;
this.profileModel = profileModel;
this.userQuery = userQuery;
this.binding = binding;
this.locationModel = locationModel;
}
@Override
public void onClick(final View v) {
// else if (v == binding.btnFollow) {
//
// } else if (v == mainActivity.mainBinding.profileView.btnRestrict && isLoggedIn) {
// new ProfileAction().execute("restrict");
// } else if (v == mainActivity.mainBinding.profileView.btnSaved && !isSelf) {
// new ProfileAction().execute("block");
// } else if (v == mainActivity.mainBinding.profileView.btnFollowTag) {
// new ProfileAction().execute("followtag");
// } else if (v == mainActivity.mainBinding.profileView.btnTagged || (v == mainActivity.mainBinding.profileView.btnRestrict && !isLoggedIn)) {
// mainActivity.startActivity(new Intent(mainActivity, SavedViewer.class)
// .putExtra(Constants.EXTRAS_INDEX, "%" + mainActivity.profileModel.getId())
// .putExtra(Constants.EXTRAS_USER, "@" + mainActivity.profileModel.getUsername())
// );
// } else if (v == mainActivity.mainBinding.profileView.btnSaved) {
//
// } else if (v == mainActivity.mainBinding.profileView.btnLiked) {
//
// }
}
}

View File

@ -0,0 +1,532 @@
package awais.instagrabber.fragments.main;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.SpannableStringBuilder;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.OnBackPressedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import java.util.Arrays;
import java.util.Collections;
import awais.instagrabber.R;
import awais.instagrabber.activities.FollowViewer;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.activities.SavedViewer;
import awais.instagrabber.adapters.PostsAdapter;
import awais.instagrabber.asyncs.HighlightsFetcher;
import awais.instagrabber.asyncs.PostsFetcher;
import awais.instagrabber.asyncs.ProfileFetcher;
import awais.instagrabber.asyncs.UsernameFetcher;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.databinding.FragmentProfileBinding;
import awais.instagrabber.fragments.main.viewmodels.ProfilePostsViewModel;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.services.ProfileService;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class ProfileFragment extends Fragment {
private static final String TAG = "ProfileFragment";
private MainActivity fragmentActivity;
private CoordinatorLayout root;
private FragmentProfileBinding binding;
private boolean isLoggedIn;
private String cookie;
private String username;
private ProfileModel profileModel;
private ProfilePostsViewModel profilePostsViewModel;
private PostsAdapter postsAdapter;
private ActionMode actionMode;
private Handler usernameSettingHandler;
private ProfileService profileService;
private final Runnable usernameSettingRunnable = () -> {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(username.substring(1));
}
};
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (postsAdapter == null) {
remove();
return;
}
postsAdapter.clearSelection();
remove();
}
};
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback(
R.menu.multi_select_download_menu,
new CallbacksHelper() {
@Override
public void onDestroy(final ActionMode mode) {
onBackPressedCallback.handleOnBackPressed();
}
@Override
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
if (item.getItemId() == R.id.action_download) {
if (postsAdapter == null || username == null) {
return false;
}
Utils.batchDownload(requireContext(),
username,
DownloadMethod.DOWNLOAD_MAIN,
postsAdapter.getSelectedModels());
checkAndResetAction();
return true;
}
return false;
}
});
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity();
profileService = ProfileService.getInstance();
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
final ViewGroup container,
final Bundle savedInstanceState) {
if (root != null) {
return root;
}
binding = FragmentProfileBinding.inflate(inflater, container, false);
root = binding.getRoot();
return root;
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
init();
}
@Override
public void onDestroy() {
super.onDestroy();
if (usernameSettingHandler != null) {
usernameSettingHandler.removeCallbacks(usernameSettingRunnable);
}
if (profilePostsViewModel != null) {
profilePostsViewModel.getList().postValue(Collections.emptyList());
}
}
private void init() {
cookie = settingsHelper.getString(Constants.COOKIE);
isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null;
if (getArguments() != null) {
final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments());
username = fragmentArgs.getUsername();
setUsernameDelayed();
}
if (!isLoggedIn) {
binding.privatePage1.setImageResource(R.drawable.ic_info);
binding.privatePage2.setText(R.string.no_acc);
binding.privatePage.setVisibility(View.VISIBLE);
return;
}
setupPosts();
fetchProfile();
}
private void fetchProfile() {
final String uid = Utils.getUserIdFromCookie(cookie);
if (username == null && uid != null) {
final FetchListener<String> fetchListener = username -> {
if (Utils.isEmpty(username)) return;
this.username = username;
setUsernameDelayed();
fetchProfileDetails();
// adds cookies to database for quick access
final DataBox.CookieModel cookieModel = Utils.dataBox.getCookie(uid);
if (Utils.dataBox.getCookieCount() == 0 || cookieModel == null || Utils.isEmpty(cookieModel.getUsername()))
Utils.dataBox.addUserCookie(new DataBox.CookieModel(uid, username, cookie));
};
boolean found = false;
final DataBox.CookieModel cookieModel = Utils.dataBox.getCookie(uid);
if (cookieModel != null) {
final String username = cookieModel.getUsername();
if (username != null) {
found = true;
fetchListener.onResult("@" + username);
}
}
if (!found) {
// if not in database, fetch info from instagram
new UsernameFetcher(uid, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
return;
}
fetchProfileDetails();
}
private void fetchProfileDetails() {
new ProfileFetcher(username.substring(1), profileModel -> {
this.profileModel = profileModel;
new PostsFetcher(profileModel.getId(),
null,
result -> profilePostsViewModel.getList().postValue(Arrays.asList(result)))
.setUsername(profileModel.getUsername())
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
setProfileDetails();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void setProfileDetails() {
setupCommonListeners();
if (profileModel == null) {
binding.swipeRefreshLayout.setRefreshing(false);
Toast.makeText(requireContext(), R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
return;
}
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
final String profileId = profileModel.getId();
if (settingsHelper.getBoolean(Constants.STORIESIG)) {
new iStoryStatusFetcher(profileId, profileModel.getUsername(), false, false,
(!isLoggedIn && settingsHelper.getBoolean(Constants.STORIESIG)), false,
result -> {
// mainActivity.storyModels = result;
// if (result != null && result.length > 0)
// binding.mainProfileImage.setStoriesBorder();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new HighlightsFetcher(profileId, (!isLoggedIn && settingsHelper.getBoolean(Constants.STORIESIG)), result -> {
if (result != null && result.length > 0) {
binding.highlightsList.setVisibility(View.VISIBLE);
// highlightsAdapter.setData(result);
} else
binding.highlightsList.setVisibility(View.GONE);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
if (isLoggedIn) {
final String myId = Utils.getUserIdFromCookie(cookie);
if (profileId.equals(myId)) {
binding.btnTagged.setVisibility(View.VISIBLE);
binding.btnSaved.setVisibility(View.VISIBLE);
binding.btnLiked.setVisibility(View.VISIBLE);
binding.btnSaved.setText(R.string.saved);
ViewCompat.setBackgroundTintList(
binding.btnSaved,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_orange_background)));
} else {
binding.btnTagged.setVisibility(View.GONE);
binding.btnSaved.setVisibility(View.GONE);
binding.btnLiked.setVisibility(View.GONE);
binding.btnFollow.setVisibility(View.VISIBLE);
if (profileModel.getFollowing()) {
binding.btnFollow.setText(R.string.unfollow);
ViewCompat.setBackgroundTintList(
binding.btnFollow,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_purple_background)));
} else if (profileModel.getRequested()) {
binding.btnFollow.setText(R.string.cancel);
ViewCompat.setBackgroundTintList(
binding.btnFollow,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_purple_background)));
} else {
binding.btnFollow.setText(R.string.follow);
ViewCompat.setBackgroundTintList(
binding.btnFollow,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_pink_background)));
}
binding.btnRestrict.setVisibility(View.VISIBLE);
if (profileModel.getRestricted()) {
binding.btnRestrict.setText(R.string.unrestrict);
ViewCompat.setBackgroundTintList(
binding.btnRestrict,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_green_background)));
} else {
binding.btnRestrict.setText(R.string.restrict);
ViewCompat.setBackgroundTintList(
binding.btnRestrict,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_orange_background)));
}
if (profileModel.isReallyPrivate()) {
binding.btnBlock.setVisibility(View.VISIBLE);
binding.btnTagged.setVisibility(View.GONE);
if (profileModel.getBlocked()) {
binding.btnBlock.setText(R.string.unblock);
ViewCompat.setBackgroundTintList(
binding.btnBlock,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_green_background)));
} else {
binding.btnBlock.setText(R.string.block);
ViewCompat.setBackgroundTintList(
binding.btnBlock,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_red_background)));
}
} else {
binding.btnBlock.setVisibility(View.GONE);
binding.btnSaved.setVisibility(View.VISIBLE);
binding.btnTagged.setVisibility(View.VISIBLE);
if (profileModel.getBlocked()) {
binding.btnSaved.setText(R.string.unblock);
ViewCompat.setBackgroundTintList(
binding.btnSaved,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_green_background)));
} else {
binding.btnSaved.setText(R.string.block);
ViewCompat.setBackgroundTintList(
binding.btnSaved,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_red_background))
);
}
}
}
} else {
if (Utils.dataBox.getFavorite(username) != null) {
binding.btnFollow.setText(R.string.unfavorite_short);
ViewCompat.setBackgroundTintList(
binding.btnFollow,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_purple_background)));
} else {
binding.btnFollow.setText(R.string.favorite_short);
ViewCompat.setBackgroundTintList(
binding.btnFollow,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_pink_background)));
}
binding.btnFollow.setVisibility(View.VISIBLE);
if (!profileModel.isReallyPrivate()) {
binding.btnRestrict.setVisibility(View.VISIBLE);
binding.btnRestrict.setText(R.string.tagged);
ViewCompat.setBackgroundTintList(
binding.btnRestrict,
ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.btn_blue_background)));
}
}
binding.mainProfileImage.setImageURI(profileModel.getSdProfilePic());
final long followersCount = profileModel.getFollowersCount();
final long followingCount = profileModel.getFollowingCount();
final String postCount = String.valueOf(profileModel.getPostCount());
SpannableStringBuilder span = new SpannableStringBuilder(getString(R.string.main_posts_count, postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
binding.mainPostCount.setText(span);
final String followersCountStr = String.valueOf(followersCount);
final int followersCountStrLen = followersCountStr.length();
span = new SpannableStringBuilder(getString(R.string.main_posts_followers, followersCountStr));
span.setSpan(new RelativeSizeSpan(1.2f), 0, followersCountStrLen, 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followersCountStrLen, 0);
binding.mainFollowers.setText(span);
final String followingCountStr = String.valueOf(followingCount);
final int followingCountStrLen = followingCountStr.length();
span = new SpannableStringBuilder(getString(R.string.main_posts_following, followingCountStr));
span.setSpan(new RelativeSizeSpan(1.2f), 0, followingCountStrLen, 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0);
binding.mainFollowing.setText(span);
binding.mainFullName.setText(Utils.isEmpty(profileModel.getName()) ? profileModel.getUsername() : profileModel.getName());
CharSequence biography = profileModel.getBiography();
binding.mainBiography.setCaptionIsExpandable(true);
binding.mainBiography.setCaptionIsExpanded(true);
if (Utils.hasMentions(biography)) {
biography = Utils.getMentionText(biography);
binding.mainBiography.setText(biography, TextView.BufferType.SPANNABLE);
// binding.mainBiography.setMentionClickListener(mentionClickListener);
} else {
binding.mainBiography.setText(biography);
binding.mainBiography.setMentionClickListener(null);
}
final String url = profileModel.getUrl();
if (Utils.isEmpty(url)) {
binding.mainUrl.setVisibility(View.GONE);
} else {
binding.mainUrl.setVisibility(View.VISIBLE);
binding.mainUrl.setText(Utils.getSpannableUrl(url));
}
binding.mainFullName.setSelected(true);
binding.mainBiography.setEnabled(true);
if (!profileModel.isReallyPrivate()) {
binding.mainFollowing.setClickable(true);
binding.mainFollowers.setClickable(true);
if (isLoggedIn) {
final View.OnClickListener followClickListener = v -> startActivity(new Intent(requireContext(), FollowViewer.class)
.putExtra(Constants.EXTRAS_FOLLOWERS, v == binding.mainFollowers)
.putExtra(Constants.EXTRAS_NAME, profileModel.getUsername())
.putExtra(Constants.EXTRAS_ID, profileId));
binding.mainFollowers.setOnClickListener(followersCount > 0 ? followClickListener : null);
binding.mainFollowing.setOnClickListener(followingCount > 0 ? followClickListener : null);
}
if (profileModel.getPostCount() == 0) {
binding.swipeRefreshLayout.setRefreshing(false);
binding.privatePage1.setImageResource(R.drawable.ic_cancel);
binding.privatePage2.setText(R.string.empty_acc);
binding.privatePage.setVisibility(View.VISIBLE);
} else {
binding.swipeRefreshLayout.setRefreshing(true);
binding.mainPosts.setVisibility(View.VISIBLE);
// currentlyExecuting = new PostsFetcher(profileId, postsFetchListener)
// .setUsername(profileModel.getUsername())
// .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
} else {
binding.mainFollowers.setClickable(false);
binding.mainFollowing.setClickable(false);
binding.swipeRefreshLayout.setRefreshing(false);
// error
binding.privatePage1.setImageResource(R.drawable.lock);
binding.privatePage2.setText(R.string.priv_acc);
binding.privatePage.setVisibility(View.VISIBLE);
binding.mainPosts.setVisibility(View.GONE);
}
}
private void setupCommonListeners() {
final String userIdFromCookie = Utils.getUserIdFromCookie(cookie);
final boolean isSelf = isLoggedIn
&& profileModel != null
&& userIdFromCookie != null
&& userIdFromCookie.equals(profileModel.getId());
final String favorite = Utils.dataBox.getFavorite(username);
binding.btnFollow.setOnClickListener(v -> {
if (!isLoggedIn) {
if (favorite != null && v == binding.btnFollow) {
Utils.dataBox.delFavorite(
new DataBox.FavoriteModel(username,
Long.parseLong(favorite.split("/")[1]),
username.replaceAll("^@", "")
)
);
} else if (v == binding.btnFollow) {
Utils.dataBox.addFavorite(
new DataBox.FavoriteModel(username, System.currentTimeMillis(),
username.replaceAll("^@", "")));
}
// onRefresh();
return;
}
profileService.followProfile(username);
});
// binding.btnRestrict.setOnClickListener(profileActionListener);
// binding.btnBlock.setOnClickListener(profileActionListener);
binding.btnSaved.setOnClickListener(v -> startActivity(new Intent(requireContext(), SavedViewer.class)
.putExtra(Constants.EXTRAS_INDEX, "$" + profileModel.getId())
.putExtra(Constants.EXTRAS_USER, "@" + profileModel.getUsername())
));
binding.btnLiked.setOnClickListener(v -> startActivity(new Intent(requireContext(), SavedViewer.class)
.putExtra(Constants.EXTRAS_INDEX, "^" + profileModel.getId())
.putExtra(Constants.EXTRAS_USER, "@" + profileModel.getUsername())
));
binding.btnTagged.setOnClickListener(v -> startActivity(new Intent(requireContext(), SavedViewer.class)
.putExtra(Constants.EXTRAS_INDEX, "%" + profileModel.getId())
.putExtra(Constants.EXTRAS_USER, "@" + profileModel.getUsername())
));
// binding.btnFollowTag.setOnClickListener(profileActionListener);
}
private void setUsernameDelayed() {
if (usernameSettingHandler == null) {
usernameSettingHandler = new Handler(Looper.getMainLooper());
}
usernameSettingHandler.postDelayed(usernameSettingRunnable, 200);
}
private void setupPosts() {
profilePostsViewModel = new ViewModelProvider(this).get(ProfilePostsViewModel.class);
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(requireContext(), Utils.convertDpToPx(110));
binding.mainPosts.setLayoutManager(layoutManager);
binding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
postsAdapter = new PostsAdapter((postModel, position) -> {
if (postsAdapter.isSelecting()) {
if (actionMode == null) return;
final String title = getString(R.string.number_selected, postsAdapter.getSelectedModels().size());
actionMode.setTitle(title);
return;
}
if (checkAndResetAction()) return;
startActivity(new Intent(requireContext(), PostViewer.class)
.putExtra(Constants.EXTRAS_INDEX, position)
.putExtra(Constants.EXTRAS_POST, postModel)
.putExtra(Constants.EXTRAS_USER, username)
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.MAIN_ITEMS));
}, (model, position) -> {
if (!postsAdapter.isSelecting()) {
checkAndResetAction();
return true;
}
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
if (onBackPressedDispatcher.hasEnabledCallbacks()) {
return true;
}
actionMode = fragmentActivity.startActionMode(multiSelectAction);
final String title = getString(R.string.number_selected, 1);
actionMode.setTitle(title);
onBackPressedDispatcher.addCallback(onBackPressedCallback);
return true;
});
binding.mainPosts.setAdapter(postsAdapter);
profilePostsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
}
private boolean checkAndResetAction() {
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher();
if (!onBackPressedDispatcher.hasEnabledCallbacks() || actionMode == null) {
return false;
}
actionMode.finish();
actionMode = null;
return true;
}
}

View File

@ -0,0 +1,19 @@
package awais.instagrabber.fragments.main.viewmodels;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;
import awais.instagrabber.models.DiscoverItemModel;
public class DiscoverItemViewModel extends ViewModel {
private MutableLiveData<List<DiscoverItemModel>> list;
public MutableLiveData<List<DiscoverItemModel>> getList() {
if (list == null) {
list = new MutableLiveData<>();
}
return list;
}
}

View File

@ -0,0 +1,19 @@
package awais.instagrabber.fragments.main.viewmodels;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;
import awais.instagrabber.models.FeedStoryModel;
public class FeedStoriesViewModel extends ViewModel {
private MutableLiveData<List<FeedStoryModel>> list;
public MutableLiveData<List<FeedStoryModel>> getList() {
if (list == null) {
list = new MutableLiveData<>();
}
return list;
}
}

View File

@ -0,0 +1,20 @@
package awais.instagrabber.fragments.main.viewmodels;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.models.FeedModel;
public class FeedViewModel extends ViewModel {
private MutableLiveData<List<FeedModel>> list;
public MutableLiveData<List<FeedModel>> getList() {
if (list == null) {
list = new MutableLiveData<>(Collections.emptyList());
}
return list;
}
}

View File

@ -0,0 +1,19 @@
package awais.instagrabber.fragments.main.viewmodels;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;
import awais.instagrabber.models.PostModel;
public class ProfilePostsViewModel extends ViewModel {
private MutableLiveData<List<PostModel>> list;
public MutableLiveData<List<PostModel>> getList() {
if (list == null) {
list = new MutableLiveData<>();
}
return list;
}
}

View File

@ -0,0 +1,19 @@
package awais.instagrabber.fragments.main.viewmodels;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;
import awais.instagrabber.models.StoryModel;
public class StoriesViewModel extends ViewModel {
private MutableLiveData<List<StoryModel>> list;
public MutableLiveData<List<StoryModel>> getList() {
if (list == null) {
list = new MutableLiveData<>();
}
return list;
}
}

View File

@ -1,14 +1,16 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import androidx.core.util.ObjectsCompat;
import java.io.Serializable;
import java.util.Date;
import awais.instagrabber.adapters.MultiSelectListAdapter.Selectable;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.utils.Utils;
public abstract class BasePostModel implements Serializable {
public abstract class BasePostModel implements Serializable, Selectable {
protected String postId;
protected String displayUrl;
protected String shortCode;
@ -88,4 +90,17 @@ public abstract class BasePostModel implements Serializable {
public final String getPostDate() {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final BasePostModel that = (BasePostModel) o;
return ObjectsCompat.equals(postId, that.postId);
}
@Override
public int hashCode() {
return ObjectsCompat.hash(postId);
}
}

View File

@ -51,4 +51,6 @@ public class PostModel extends BasePostModel {
this.endCursor = endCursor;
this.hasNextPage = hasNextPage;
}
}

View File

@ -0,0 +1,4 @@
package awais.instagrabber.repositories;
public interface ProfileRepository {
}

View File

@ -0,0 +1,18 @@
package awais.instagrabber.repositories;
import java.util.Map;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.QueryMap;
import retrofit2.http.Url;
public interface StoriesRepository {
@GET("graphql/query/")
Call<String> getStories(@QueryMap(encoded = true) Map<String, String> variables);
@GET
Call<String> getUserStory(@Header("User-Agent") String userAgent, @Url String url);
}

View File

@ -0,0 +1,24 @@
package awais.instagrabber.services;
import androidx.annotation.NonNull;
import java.io.IOException;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class AddCookiesInterceptor implements Interceptor {
@NonNull
@Override
public Response intercept(@NonNull final Chain chain) throws IOException {
final Request request = chain.request();
final Request.Builder builder = request.newBuilder();
final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
builder.addHeader("Cookie", cookie);
final Request updatedRequest = builder.build();
return chain.proceed(updatedRequest);
}
}

View File

@ -0,0 +1,24 @@
package awais.instagrabber.services;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public abstract class BaseService {
private Retrofit.Builder builder;
Retrofit.Builder getRetrofitBuilder() {
if (builder == null) {
final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
.followRedirects(false)
.followSslRedirects(false)
.build();
builder = new Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.client(client);
}
return builder;
}
}

View File

@ -0,0 +1,4 @@
package awais.instagrabber.services;
public interface FeedService {
}

View File

@ -0,0 +1,35 @@
package awais.instagrabber.services;
import awais.instagrabber.repositories.ProfileRepository;
import retrofit2.Retrofit;
public class ProfileService extends BaseService {
private static final String TAG = "ProfileService";
private final ProfileRepository repository;
private static ProfileService instance;
private ProfileService() {
final Retrofit retrofit = getRetrofitBuilder()
.baseUrl("https://i.instagram.com")
.build();
repository = retrofit.create(ProfileRepository.class);
}
public static ProfileService getInstance() {
if (instance == null) {
instance = new ProfileService();
}
return instance;
}
public void followProfile(final String username) {
// final String url = "https://www.instagram.com/web/" + (action.equals("followtag") && mainActivity.hashtagModel != null ? "tags/" + (mainActivity.hashtagModel.getFollowing() ? "unfollow/" : "follow/") + mainActivity.hashtagModel.getName() + "/" : (action.equals("restrict") && mainActivity.profileModel != null ? "restrict_action" : "friendships/" + mainActivity.profileModel.getId()) + "/" + (action.equals("follow") ?
// mainActivity.profileModel.getFollowing() || mainActivity.profileModel.getRequested()
// ? "unfollow/" : "follow/" :
// action.equals("restrict") ?
// mainActivity.profileModel.getRestricted() ? "unrestrict/" : "restrict/" :
// mainActivity.profileModel.getBlocked() ? "unblock/" : "block/"));
}
}

View File

@ -0,0 +1,7 @@
package awais.instagrabber.services;
public interface ServiceCallback<T> {
void onSuccess(T result);
void onFailure(Throwable t);
}

View File

@ -0,0 +1,277 @@
package awais.instagrabber.services;
import android.util.Log;
import androidx.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.stickers.PollModel;
import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
import awais.instagrabber.repositories.StoriesRepository;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class StoriesService extends BaseService {
private static final String TAG = "StoriesService";
private final StoriesRepository repository;
private static StoriesService instance;
private StoriesService() {
final Retrofit retrofit = getRetrofitBuilder()
.baseUrl("https://www.instagram.com")
.build();
repository = retrofit.create(StoriesRepository.class);
}
public static StoriesService getInstance() {
if (instance == null) {
instance = new StoriesService();
}
return instance;
}
public void getFeedStories(final ServiceCallback<List<FeedStoryModel>> callback) {
final Map<String, String> queryMap = new HashMap<>();
queryMap.put("query_hash", "b7b84d884400bc5aa7cfe12ae843a091");
queryMap.put("variables", "{\"only_stories\":true,\"stories_prefetch\":false,\"stories_video_dash_manifest\":false}");
final Call<String> response = repository.getStories(queryMap);
response.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
final String body = response.body();
if (body == null) {
Log.e(TAG, "getFeedStories: body is empty");
return;
}
try {
final List<FeedStoryModel> feedStoryModels = new ArrayList<>();
final JSONArray feedStoriesReel = new JSONObject(body)
.getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER)
.getJSONObject("feed_reels_tray")
.getJSONObject("edge_reels_tray_to_reel")
.getJSONArray("edges");
for (int i = 0; i < feedStoriesReel.length(); ++i) {
final JSONObject node = feedStoriesReel.getJSONObject(i).getJSONObject("node");
final JSONObject user = node.getJSONObject(node.has("user") ? "user" : "owner");
final ProfileModel profileModel = new ProfileModel(false, false, false,
user.getString("id"),
user.getString("username"),
null, null, null,
user.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
final String id = node.getString("id");
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media");
feedStoryModels.add(new FeedStoryModel(id, profileModel, fullyRead));
}
callback.onSuccess(feedStoryModels);
} catch (JSONException e) {
Log.e(TAG, "Error parsing json", e);
}
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
callback.onFailure(t);
}
});
}
public void getUserStory(final String id,
final String username,
final boolean storiesig,
final boolean isLoc,
final boolean isHashtag,
final boolean highlight,
final ServiceCallback<List<StoryModel>> callback) {
final String url = buildUrl(id, storiesig, isLoc, isHashtag, highlight);
final String userAgent = storiesig ? Constants.A_USER_AGENT : Constants.I_USER_AGENT;
final Call<String> userStoryCall = repository.getUserStory(userAgent, url);
userStoryCall.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
JSONObject data;
String localUsername = username;
try {
final String body = response.body();
if (body == null) {
Log.e(TAG, "body is null");
return;
}
data = new JSONObject(body);
if (!storiesig && !highlight)
data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel");
else if (highlight) data = data.getJSONObject("reels").optJSONObject(id);
if (data != null
&& localUsername == null
&& !isLoc
&& !isHashtag)
localUsername = data.getJSONObject("user").getString("username");
JSONArray media;
if (data != null
&& (media = data.optJSONArray("items")) != null
&& media.length() > 0 && media.optJSONObject(0) != null) {
final int mediaLen = media.length();
final List<StoryModel> models = new ArrayList<>();
for (int i = 0; i < mediaLen; ++i) {
data = media.getJSONObject(i);
final boolean isVideo = data.has("video_duration");
final StoryModel model = new StoryModel(data.getString("id"),
data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0).getString("url"),
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
data.optLong("taken_at", 0),
(isLoc || isHashtag) ? data.getJSONObject("user").getString("username") : localUsername,
data.getJSONObject("user").getString("pk"),
data.getBoolean("can_reply"));
final JSONArray videoResources = data.optJSONArray("video_versions");
if (isVideo && videoResources != null)
model.setVideoUrl(Utils.getHighQualityPost(videoResources, true, true, false));
if (data.has("story_feed_media")) {
model.setTappableShortCode(data.getJSONArray("story_feed_media").getJSONObject(0).optString("media_id"));
}
if (!data.isNull("story_app_attribution"))
model.setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]);
if (data.has("story_polls")) {
final JSONArray storyPolls = data.optJSONArray("story_polls");
JSONObject tappableObject = null;
if (storyPolls != null) {
tappableObject = storyPolls.getJSONObject(0).optJSONObject("poll_sticker");
}
if (tappableObject != null) model.setPoll(new PollModel(
String.valueOf(tappableObject.getLong("poll_id")),
tappableObject.getString("question"),
tappableObject.getJSONArray("tallies").getJSONObject(0).getString("text"),
tappableObject.getJSONArray("tallies").getJSONObject(0).getInt("count"),
tappableObject.getJSONArray("tallies").getJSONObject(1).getString("text"),
tappableObject.getJSONArray("tallies").getJSONObject(1).getInt("count"),
tappableObject.optInt("viewer_vote", -1)
));
}
if (data.has("story_questions")) {
JSONObject tappableObject = data.getJSONArray("story_questions").getJSONObject(0).optJSONObject("question_sticker");
if (tappableObject != null && !tappableObject.getString("question_type").equals("music"))
model.setQuestion(new QuestionModel(
String.valueOf(tappableObject.getLong("question_id")),
tappableObject.getString("question")
));
}
if (data.has("story_quizs")) {
JSONObject tappableObject = data.getJSONArray("story_quizs").getJSONObject(0).optJSONObject("quiz_sticker");
if (tappableObject != null) {
String[] choices = new String[tappableObject.getJSONArray("tallies").length()];
Long[] counts = new Long[choices.length];
for (int q = 0; q < choices.length; ++q) {
JSONObject tempchoice = tappableObject.getJSONArray("tallies").getJSONObject(q);
choices[q] = (q == tappableObject.getInt("correct_answer") ? "*** " : "")
+ tempchoice.getString("text");
counts[q] = tempchoice.getLong("count");
}
model.setQuiz(new QuizModel(
String.valueOf(tappableObject.getLong("quiz_id")),
tappableObject.getString("question"),
choices,
counts,
tappableObject.optInt("viewer_answer", -1)
));
}
}
JSONArray hashtags = data.optJSONArray("story_hashtags");
JSONArray locations = data.optJSONArray("story_locations");
JSONArray atmarks = data.optJSONArray("reel_mentions");
String[] mentions = new String[(hashtags == null ? 0 : hashtags.length())
+ (atmarks == null ? 0 : atmarks.length())
+ (locations == null ? 0 : locations.length())];
if (hashtags != null) {
for (int h = 0; h < hashtags.length(); ++h) {
mentions[h] = "#" + hashtags.getJSONObject(h).getJSONObject("hashtag").getString("name");
}
}
if (atmarks != null) {
for (int h = 0; h < atmarks.length(); ++h) {
mentions[h + (hashtags == null ? 0 : hashtags.length())] =
"@" + atmarks.getJSONObject(h).getJSONObject("user").getString("username");
}
}
if (locations != null) {
for (int h = 0; h < locations.length(); ++h) {
mentions[h + (hashtags == null ? 0 : hashtags.length()) + (atmarks == null ? 0 : atmarks.length())] =
locations.getJSONObject(h).getJSONObject("location").getLong("pk")
+ "/ (" + locations.getJSONObject(h).getJSONObject("location").getString("short_name") + ")";
}
}
if (mentions.length != 0) model.setMentions(mentions);
models.add(model);
}
callback.onSuccess(models);
}
} catch (JSONException e) {
Log.e(TAG, "Error parsing string");
}
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
callback.onFailure(t);
}
});
}
private String buildUrl(final String id, final boolean storiesig, final boolean isLoc, final boolean isHashtag, final boolean highlight) {
final String userId = id.replace(":", "%3A");
final StringBuilder builder = new StringBuilder();
builder.append("https://");
if (storiesig) {
builder.append("storiesig");
} else {
builder.append("i.instagram");
}
builder.append(".com/api/v1/");
if (isLoc) {
builder.append("locations/");
}
if (isHashtag) {
builder.append("tags/");
}
if (highlight) {
builder.append("feed/reels_media?user_ids=");
} else {
builder.append("feed/user/");
}
builder.append(userId);
if (!highlight) {
if (storiesig) {
builder.append("/reel_media/");
} else {
builder.append("/story/");
}
}
return builder.toString();
}
}

View File

@ -0,0 +1,207 @@
package awais.instagrabber.utils;
import android.content.Intent;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.navigation.NavController;
import androidx.navigation.NavGraph;
import androidx.navigation.fragment.NavHostFragment;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.util.List;
import awais.instagrabber.R;
public class NavigationExtensions {
@NonNull
public static LiveData<NavController> setupWithNavController(@NonNull final BottomNavigationView bottomNavigationView,
@NonNull List<Integer> navGraphIds,
@NonNull final FragmentManager fragmentManager,
final int containerId,
@NonNull Intent intent,
final int firstFragmentGraphIndex) {
final SparseArray<String> graphIdToTagMap = new SparseArray<>();
final MutableLiveData<NavController> selectedNavController = new MutableLiveData<>();
int firstFragmentGraphId = 0;
for (int i = 0; i < navGraphIds.size(); i++) {
final int navGraphId = navGraphIds.get(i);
final String fragmentTag = getFragmentTag(i);
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);
final NavController navController = navHostFragment.getNavController();
final int graphId = navController.getGraph().getId();
if (i == firstFragmentGraphIndex) {
firstFragmentGraphId = graphId;
}
graphIdToTagMap.put(graphId, fragmentTag);
if (bottomNavigationView.getSelectedItemId() == graphId) {
selectedNavController.setValue(navHostFragment.getNavController());
attachNavHostFragment(fragmentManager, navHostFragment, i == firstFragmentGraphIndex);
} else {
detachNavHostFragment(fragmentManager, navHostFragment);
}
}
final String[] selectedItemTag = {graphIdToTagMap.get(bottomNavigationView.getSelectedItemId())};
final String firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId);
final boolean[] isOnFirstFragment = {selectedItemTag[0] != null && selectedItemTag[0].equals(firstFragmentTag)};
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
if (fragmentManager.isStateSaved()) {
return false;
}
String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());
if (!selectedItemTag[0].equals(newlySelectedItemTag)) {
fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag);
if (fragment == null) {
throw new RuntimeException("null cannot be cast to non-null NavHostFragment");
}
final NavHostFragment selectedFragment = (NavHostFragment) fragment;
if (!firstFragmentTag.equals(newlySelectedItemTag)) {
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction()
.setCustomAnimations(
R.anim.nav_default_enter_anim,
R.anim.nav_default_exit_anim,
R.anim.nav_default_pop_enter_anim,
R.anim.nav_default_pop_exit_anim
)
.attach(selectedFragment)
.setPrimaryNavigationFragment(selectedFragment);
for (int i = 0; i < graphIdToTagMap.size(); i++) {
final int key = graphIdToTagMap.keyAt(i);
final String fragmentTagForId = graphIdToTagMap.get(key);
if (!fragmentTagForId.equals(newlySelectedItemTag)) {
final Fragment fragmentByTag = fragmentManager.findFragmentByTag(firstFragmentTag);
if (fragmentByTag == null) {
continue;
}
fragmentTransaction.detach(fragmentByTag);
}
}
fragmentTransaction.addToBackStack(firstFragmentTag)
.setReorderingAllowed(true)
.commit();
}
selectedItemTag[0] = newlySelectedItemTag;
isOnFirstFragment[0] = selectedItemTag[0].equals(firstFragmentTag);
selectedNavController.setValue(selectedFragment.getNavController());
return true;
}
return false;
});
setupItemReselected(bottomNavigationView, graphIdToTagMap, fragmentManager);
setupDeepLinks(bottomNavigationView, navGraphIds, fragmentManager, containerId, intent);
final int finalFirstFragmentGraphId = firstFragmentGraphId;
fragmentManager.addOnBackStackChangedListener(() -> {
if (!isOnFirstFragment[0]) {
if (firstFragmentTag == null) {
return;
}
if (!isOnBackStack(fragmentManager, firstFragmentTag)) {
bottomNavigationView.setSelectedItemId(finalFirstFragmentGraphId);
}
}
final NavController navController = selectedNavController.getValue();
if (navController != null && navController.getCurrentDestination() == null) {
final NavGraph navControllerGraph = navController.getGraph();
navController.navigate(navControllerGraph.getId());
}
});
return selectedNavController;
}
private static NavHostFragment obtainNavHostFragment(final FragmentManager fragmentManager,
final String fragmentTag,
final int navGraphId,
final int containerId) {
final NavHostFragment existingFragment = (NavHostFragment) fragmentManager.findFragmentByTag(fragmentTag);
if (existingFragment != null) {
return existingFragment;
}
final NavHostFragment navHostFragment = NavHostFragment.create(navGraphId);
fragmentManager.beginTransaction()
.add(containerId, navHostFragment, fragmentTag)
.commitNow();
return navHostFragment;
}
private static void attachNavHostFragment(final FragmentManager fragmentManager,
final NavHostFragment navHostFragment,
final boolean isPrimaryNavFragment) {
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
.attach(navHostFragment);
if (isPrimaryNavFragment) {
fragmentTransaction.setPrimaryNavigationFragment(navHostFragment);
}
fragmentTransaction.commitNow();
}
private static void detachNavHostFragment(final FragmentManager fragmentManager, final NavHostFragment navHostFragment) {
fragmentManager.beginTransaction()
.detach(navHostFragment)
.commitNow();
}
private static void setupItemReselected(final BottomNavigationView bottomNavigationView,
final SparseArray<String> graphIdToTagMap,
final FragmentManager fragmentManager) {
bottomNavigationView.setOnNavigationItemReselectedListener(item -> {
final String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());
final Fragment fragmentByTag = fragmentManager.findFragmentByTag(newlySelectedItemTag);
if (fragmentByTag == null) {
throw new NullPointerException("null cannot be cast to non-null type NavHostFragment");
}
final NavHostFragment selectedFragment = (NavHostFragment) fragmentByTag;
final NavController navController = selectedFragment.getNavController();
final NavGraph navControllerGraph = navController.getGraph();
navController.popBackStack(navControllerGraph.getStartDestination(), false);
});
}
private static void setupDeepLinks(final BottomNavigationView bottomNavigationView,
final List<Integer> navGraphIds,
final FragmentManager fragmentManager,
final int containerId,
final Intent intent) {
for (int i = 0; i < navGraphIds.size(); i++) {
final int navGraphId = navGraphIds.get(i);
final String fragmentTag = getFragmentTag(i);
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);
if (navHostFragment.getNavController().handleDeepLink(intent)) {
final int selectedItemId = bottomNavigationView.getSelectedItemId();
NavController navController = navHostFragment.getNavController();
NavGraph graph = navController.getGraph();
if (selectedItemId != graph.getId()) {
navController = navHostFragment.getNavController();
graph = navController.getGraph();
bottomNavigationView.setSelectedItemId(graph.getId());
}
}
}
}
private static boolean isOnBackStack(final FragmentManager fragmentManager, final String backStackName) {
int backStackCount = fragmentManager.getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) {
final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i);
final String name = backStackEntry.getName();
if (name != null && name.equals(backStackName)) {
return true;
}
}
return false;
}
private static String getFragmentTag(final int index) {
return "bottomNavigation#" + index;
}
}

View File

@ -71,6 +71,7 @@ import javax.crypto.spec.SecretKeySpec;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.activities.MainActivityBackup;
import awais.instagrabber.activities.ProfileViewer;
import awais.instagrabber.activities.SavedViewer;
import awais.instagrabber.asyncs.DownloadAsync;
@ -942,7 +943,7 @@ public final class Utils {
dir = new File(dir, username);
if (dir.exists() || dir.mkdirs()) {
final MainActivity mainActivity = method != DownloadMethod.DOWNLOAD_FEED && context instanceof MainActivity ? (MainActivity) context : null;
final MainActivityBackup mainActivity = method != DownloadMethod.DOWNLOAD_FEED && context instanceof MainActivityBackup ? (MainActivityBackup) context : null;
final ProfileViewer pv = method == DownloadMethod.DOWNLOAD_MAIN && context instanceof ProfileViewer ? (ProfileViewer) context : null;
final SavedViewer saved = method == DownloadMethod.DOWNLOAD_SAVED && context instanceof SavedViewer ? (SavedViewer) context : null;
@ -973,19 +974,20 @@ public final class Utils {
saveFile,
file -> {
model.setDownloaded(true);
if (saved != null)
saved.deselectSelection(selectedItem);
else if (mainActivity != null)
mainActivity.mainHelper.deselectSelection(selectedItem);
else if (pv != null) pv.deselectSelection(selectedItem);
// if (saved != null)
// saved.deselectSelection(selectedItem);
// else if (mainActivity != null)
// mainActivity.mainHelper.deselectSelection(selectedItem);
// else pv.deselectSelection(selectedItem);
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
} else {
if (saved != null) saved.deselectSelection(selectedItem);
else if (mainActivity != null)
mainActivity.mainHelper.deselectSelection(selectedItem);
else if (pv != null) pv.deselectSelection(selectedItem);
}
// else {
// if (saved != null) saved.deselectSelection(selectedItem);
// else if (mainActivity != null)
// mainActivity.mainHelper.deselectSelection(selectedItem);
// else if (pv != null) pv.deselectSelection(selectedItem);
// }
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,17.17L18.83,16H4V4h16v13.17zM20,2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4V4c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,6.5c3.79,0 7.17,2.13 8.82,5.5 -1.65,3.37 -5.02,5.5 -8.82,5.5S4.83,15.37 3.18,12C4.83,8.63 8.21,6.5 12,6.5m0,-2C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,9.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5 -2.5,-1.12 -2.5,-2.5 1.12,-2.5 2.5,-2.5m0,-2c-2.48,0 -4.5,2.02 -4.5,4.5s2.02,4.5 4.5,4.5 4.5,-2.02 4.5,-4.5 -2.02,-4.5 -4.5,-4.5z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,104 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:context=".activities.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/layout_include_toolbar" />
<awais.instagrabber.customviews.RemixDrawerLayout
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- Profile View -->
<include
android:id="@+id/profile_view"
layout="@layout/layout_profile_view" />
<!-- Feed View -->
<include
android:id="@+id/feed_view"
layout="@layout/layout_feed_view" />
<!-- Discover View -->
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/discoverLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end"
android:animateLayoutChanges="true"
android:tag="@android:string/yes">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
app:elevation="0dp">
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|snap|enterAlways">
app:layout_scrollFlags="scroll|snap|enterAlways"
app:titleEnabled="false">
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/discoverType"
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Widget.MaterialComponents.Toolbar.PrimarySurface"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/discover_placeholder"/>
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="none"
app:popupTheme="@style/Widget.MaterialComponents.Toolbar.PrimarySurface"
app:title="@string/app_name" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/discoverSwipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:tag="@android:string/yes"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/discoverPosts"
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_nav_host"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/item_feed_photo" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</awais.instagrabber.customviews.RemixDrawerLayout>
</LinearLayout>
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<LinearLayout
android:id="@+id/iconSlider"
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="20dp"
android:layout_height="20dp"
app:srcCompat="@drawable/ic_feed"
app:tint="?android:textColorSecondary" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="20dp"
android:layout_height="20dp"
app:srcCompat="@drawable/ic_profile"
app:tint="?android:textColorPrimary" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="20dp"
android:layout_height="20dp"
app:srcCompat="@drawable/ic_discover"
app:tint="?android:textColorSecondary" />
</LinearLayout>
</FrameLayout>
android:layout_gravity="bottom"
app:labelVisibilityMode="labeled"
app:menu="@menu/main_bottom_navigation_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/layout_include_toolbar" />
<awais.instagrabber.customviews.RemixDrawerLayout
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- Profile View -->
<include
android:id="@+id/profile_view"
layout="@layout/layout_profile_view" />
<!-- Feed View -->
<include
android:id="@+id/feed_view"
layout="@layout/layout_feed_view" />
<!-- Discover View -->
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/discoverLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end"
android:animateLayoutChanges="true"
android:tag="@android:string/yes">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|snap|enterAlways">
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/discoverType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/discover_placeholder"/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/discoverSwipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:tag="@android:string/yes"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/discoverPosts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/item_feed_photo" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</awais.instagrabber.customviews.RemixDrawerLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/iconSlider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="20dp"
android:layout_height="20dp"
app:srcCompat="@drawable/ic_feed"
app:tint="?android:textColorSecondary" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="20dp"
android:layout_height="20dp"
app:srcCompat="@drawable/ic_profile"
app:tint="?android:textColorPrimary" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="20dp"
android:layout_height="20dp"
app:srcCompat="@drawable/ic_discover"
app:tint="?android:textColorSecondary" />
</LinearLayout>
</FrameLayout>

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activities.PostViewer">
<include
android:id="@+id/toolbar"
layout="@layout/layout_include_toolbar" />
android:orientation="vertical">
<FrameLayout
android:id="@+id/story_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
android:layout_marginTop="?attr/actionBarSize"
android:layout_weight="1"
app:layout_constraintBottom_toTopOf="@id/storiesList"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/playerView"
@ -25,7 +25,7 @@
app:controller_layout_id="@layout/layout_controls"
app:repeat_toggle_modes="none" />
<com.github.chrisbanes.photoview.PhotoView
<awais.instagrabber.customviews.drawee.ZoomableDraweeView
android:id="@+id/imageViewer"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -42,9 +42,9 @@
android:id="@+id/postActions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.3"
android:background="#0000"
android:layout_gravity="bottom">
android:layout_gravity="bottom"
android:background="#0000">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/viewStoryPost"
android:layout_width="match_parent"
@ -54,6 +54,7 @@
android:textColor="@color/btn_green_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_green_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/poll"
android:layout_width="match_parent"
@ -63,6 +64,7 @@
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/answer"
android:layout_width="match_parent"
@ -72,6 +74,7 @@
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/quiz"
android:layout_width="match_parent"
@ -81,6 +84,7 @@
android:textColor="@color/btn_blue_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/mention"
android:layout_width="match_parent"
@ -90,6 +94,7 @@
android:textColor="@color/btn_orange_text_color"
android:visibility="gone"
app:backgroundTint="@color/btn_orange_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/spotify"
android:layout_width="match_parent"
@ -107,9 +112,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="5dp"
android:paddingLeft="5dp"
android:paddingEnd="5dp"
android:paddingRight="5dp" />
</LinearLayout>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,10 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefreshLayout"
<awais.instagrabber.customviews.helpers.NestedCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorOnPrimarySurface"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/inbox_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/layout_include_simple_item" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>

View File

@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
@ -14,7 +15,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/messageList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/layout_dm_base"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<awais.instagrabber.customviews.helpers.NestedCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="?attr/colorOnPrimarySurface">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorOnPrimarySurface"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|snap|enterAlways">
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/discoverType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/discover_placeholder"
android:spinnerMode="dialog" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/discoverSwipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/discoverPosts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/item_post" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<awais.instagrabber.customviews.helpers.NestedCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="?attr/colorOnPrimarySurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/stories_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|snap">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/feed_stories_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/feed_swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/feed_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/item_feed_photo" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>

View File

@ -0,0 +1,304 @@
<?xml version="1.0" encoding="utf-8"?>
<awais.instagrabber.customviews.helpers.NestedCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorOnPrimarySurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorOnPrimarySurface">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll">
<RelativeLayout
android:id="@+id/infoContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical"
android:paddingBottom="5dp">
<LinearLayout
android:id="@+id/profileInfoText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/profileInfo"
android:orientation="horizontal"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:paddingEnd="10dp"
android:paddingRight="10dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/isVerified"
android:layout_width="18dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:visibility="gone"
app:srcCompat="@drawable/verified" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainFullName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="marquee"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textSize="16sp"
tools:text="Austin Huang" />
</LinearLayout>
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/mainBiography"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/profileInfoText"
android:background="?android:selectableItemBackground"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:paddingEnd="10dp"
android:paddingRight="10dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="16sp"
tools:text="THE GLORIOUS (step)OWNER OF THIS APP" />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/mainUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/mainBiography"
android:ellipsize="marquee"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:paddingEnd="10dp"
android:paddingRight="10dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="16sp"
android:visibility="gone"
tools:text="https://austinhuang.me/" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/profileActions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/mainUrl">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnFollow"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="1dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="1dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/follow"
android:textColor="@color/btn_pink_text_color"
android:textSize="16sp"
android:visibility="gone"
app:backgroundTint="@color/btn_pink_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnRestrict"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="1dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="1dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/restrict"
android:textColor="@color/btn_orange_text_color"
android:textSize="16sp"
android:visibility="gone"
app:backgroundTint="@color/btn_orange_background" />
<!-- only for invisible private accounts -->
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnBlock"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="1dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="1dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/block"
android:textColor="@color/btn_red_text_color"
android:textSize="16sp"
android:visibility="gone"
app:backgroundTint="@color/btn_red_background" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/myActions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/profileActions">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnTagged"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="1dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/tagged"
android:textColor="@color/btn_blue_text_color"
android:textSize="16sp"
android:visibility="gone"
app:backgroundTint="@color/btn_blue_background" />
<!-- also used as block -->
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnSaved"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="1dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/saved"
android:textColor="@color/btn_orange_text_color"
android:textSize="16sp"
android:visibility="gone"
app:backgroundTint="@color/btn_orange_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLiked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="1dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/liked"
android:textColor="@color/btn_lightpink_text_color"
android:textSize="16sp"
android:visibility="gone"
app:backgroundTint="@color/btn_lightpink_background" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/highlightsList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/myActions"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="5dp"
android:paddingLeft="5dp"
android:paddingEnd="5dp"
android:paddingRight="5dp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/profileInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="@dimen/profile_info_container_bottom_space">
<awais.instagrabber.customviews.CircularImageView
android:id="@+id/mainProfileImage"
android:layout_width="@dimen/profile_picture_size"
android:layout_height="@dimen/profile_picture_size" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainPostCount"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_marginLeft="12dp"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:layout_weight="1"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat"
android:textSize="15sp"
tools:text="35\nPosts" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainFollowers"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?selectableItemBackgroundBorderless"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat"
android:textSize="15sp"
tools:text="68\nFollowers" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainFollowing"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:layout_marginLeft="12dp"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:layout_weight="1"
android:background="?selectableItemBackgroundBorderless"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat"
android:textSize="15sp"
tools:text="64\nFollowing" />
</LinearLayout>
</RelativeLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mainPosts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/item_post" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/privatePage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/private_page_margins"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/privatePage1"
android:layout_width="@dimen/private_page_margins"
android:layout_height="@dimen/private_page_margins"
app:srcCompat="@drawable/lock" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/privatePage2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/priv_acc"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>

View File

@ -1,117 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/top_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/btnComments"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_height="match_parent"
android:background="?android:selectableItemBackground"
android:orientation="horizontal">
android:gravity="center"
android:orientation="horizontal"
android:padding="4dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="32dp"
android:layout_height="48dp"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
app:srcCompat="@drawable/comments" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
app:srcCompat="@drawable/ic_outline_comments_24"
app:tint="?android:textColorPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/commentsCount"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:padding="2dp"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:textAppearance="?attr/textAppearanceButton"
tools:text="690000" />
</LinearLayout>
<LinearLayout
android:id="@+id/videoViewsContainer"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_toEndOf="@id/btnComments"
android:layout_toRightOf="@id/btnComments"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="32dp"
android:layout_height="48dp"
app:srcCompat="@drawable/video_views" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
app:srcCompat="@drawable/ic_outline_views_24"
app:tint="?android:textColorPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvVideoViews"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:padding="2dp"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:textAppearance="?attr/textAppearanceButton"
tools:text="690000" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_alignEnd="@id/btnMute"
android:layout_alignRight="@id/btnMute"
android:layout_marginEnd="48dp"
android:layout_marginRight="48dp"
android:layout_toEndOf="@id/videoViewsContainer"
android:layout_toRightOf="@id/videoViewsContainer">
<View
android:layout_width="1dip"
android:layout_height="match_parent"
android:layout_marginStart="2dp"
android:layout_marginLeft="2dp"
android:layout_marginEnd="2dp"
android:layout_marginRight="2dp"
android:background="?android:attr/dividerVertical" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvPostDate"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:padding="4dp"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:maxLines="1"
android:padding="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="690000" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btnMute"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignWithParentIfMissing="true"
android:layout_toStartOf="@id/btnDownload"
android:layout_toLeftOf="@id/btnDownload"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:padding="8dp"
android:visibility="gone"
app:srcCompat="@drawable/vol" />
android:padding="4dp"
android:visibility="visible"
app:srcCompat="@drawable/ic_volume_up_24"
app:tint="?android:textColorPrimary" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btnDownload"
android:layout_width="40dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp"
app:srcCompat="@drawable/ic_download" />
app:srcCompat="@drawable/ic_download"
app:tint="?android:textColorPrimary" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/btnDownload"
android:layout_height="0dp"
android:layout_weight="1"
android:isScrollContainer="true"
android:scrollbars="vertical"
android:scrollHorizontally="false">
@ -129,10 +112,13 @@
android:layout_height="match_parent"
android:background="?android:selectableItemBackground"
android:clipToPadding="false"
android:padding="12dp"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:paddingBottom="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="16sp"
tools:text="BOTTOM TEXT" />
</FrameLayout>
</ScrollView>
</RelativeLayout>
</LinearLayout>

View File

@ -8,11 +8,6 @@
android:id="@+id/item_feed_top"
layout="@layout/item_feed_top" />
<!--<com.github.chrisbanes.photoview.PhotoView-->
<!-- android:id="@+id/imageViewer"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content" />-->
<awais.instagrabber.customviews.drawee.ZoomableDraweeView
android:id="@+id/imageViewer"
android:layout_width="match_parent"

View File

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="horizontal"
android:padding="8dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/ivProfilePic"
android:layout_width="@dimen/feed_profile_size"
android:layout_height="@dimen/feed_profile_size"
app:roundAsCircle="true"
android:background="?selectableItemBackgroundBorderless" />
android:background="?selectableItemBackgroundBorderless"
app:roundAsCircle="true" />
<RelativeLayout
android:id="@+id/infoContainer"
@ -21,10 +23,10 @@
android:animateLayoutChanges="true"
android:background="@null"
android:orientation="vertical"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:paddingEnd="4dp"
android:paddingRight="4dp"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:weightSum="2">
<awais.instagrabber.customviews.RamboTextView
@ -32,8 +34,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/feed_text_primary_color"
android:textSize="18sp" />
tools:text="username" />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/location"
@ -41,14 +44,17 @@
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/feed_text_primary_color"
android:textSize="18sp" />
tools:text="location" />
</RelativeLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/viewStoryPost"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
app:srcCompat="@android:drawable/ic_menu_set_as" />
app:srcCompat="@drawable/ic_open_in_new_24"
app:tint="?android:textColorPrimary" />
</LinearLayout>

View File

@ -3,11 +3,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/profile_picture_size"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_margin="8dp"
android:background="?android:selectableItemBackground"
android:orientation="vertical"
tools:viewBindingIgnore="true">
android:orientation="vertical">
<awais.instagrabber.customviews.CircularImageView
android:id="@+id/icon"
@ -20,11 +18,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:lines="1"
android:maxLines="1"
android:padding="2dp"
android:singleLine="true"
android:text="@string/app_name"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
android:textColor="?android:textColorPrimaryInverse"
android:textStyle="bold"
tools:text="@string/app_name" />
</LinearLayout>

View File

@ -1,18 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:selectableItemBackground"
tools:viewBindingIgnore="true">
android:foreground="?android:selectableItemBackground">
<awais.instagrabber.customviews.FixedImageView
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/postImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter" />
app:actualImageScaleType="fitCenter"
app:viewAspectRatio="1"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/isDownloaded"
@ -31,12 +29,12 @@
android:layout_margin="8dp"
app:srcCompat="@drawable/video" />
<ProgressBar
android:id="@+id/progressView"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="center"
android:visibility="gone" />
<!--<ProgressBar-->
<!-- android:id="@+id/progressView"-->
<!-- android:layout_width="45dp"-->
<!-- android:layout_height="45dp"-->
<!-- android:layout_gravity="center"-->
<!-- android:visibility="gone" />-->
<FrameLayout
android:id="@+id/selectedView"

View File

@ -1,24 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/story_item_width"
android:layout_height="@dimen/story_item_height"
android:layout_marginStart="2dp"
android:layout_marginLeft="2dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="2dp"
android:layout_marginRight="2dp"
android:layout_marginBottom="4dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:selectableItemBackground"
tools:viewBindingIgnore="true">
android:layout_margin="8dp"
android:foreground="?android:selectableItemBackground">
<androidx.appcompat.widget.AppCompatImageView
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter" />
app:actualImageScaleType="fitCenter" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/selectedView"

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/direct_messages_nav_graph"
android:icon="@drawable/ic_send_24"
android:title="@string/title_dm" />
<item
android:id="@+id/feed_nav_graph"
android:icon="@drawable/ic_feed"
android:title="@string/feed" />
<item
android:id="@+id/profile_nav_graph"
android:icon="@drawable/ic_profile"
android:title="@string/profile" />
<item
android:id="@+id/discover_nav_graph"
android:icon="@drawable/ic_discover"
android:title="@string/title_discover" />
<!--<item-->
<!-- android:id="@+id/favouritesFragment"-->
<!-- android:icon="@drawable/ic_star_24"-->
<!-- android:title="@string/title_favorites"/>-->
<item
android:id="@+id/moreFragment"
android:icon="@drawable/ic_more_horiz_24"
android:title="@string/more" />
</menu>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/favourites"
android:enabled="true"
android:icon="@drawable/ic_star_24"
android:title="@string/title_favorites"
app:showAsAction="ifRoom" />
<!--<item-->
<!-- android:id="@+id/direct_messages"-->
<!-- android:enabled="true"-->
<!-- android:icon="@drawable/ic_send_24"-->
<!-- android:title="@string/action_dms"-->
<!-- app:showAsAction="always" />-->
</menu>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_download"
android:icon="@drawable/ic_download"
android:title="@string/action_download"
android:titleCondensed="@string/action_download"
app:showAsAction="always" />
</menu>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_download"
android:icon="@drawable/ic_download"
android:title="@string/action_download"
android:titleCondensed="@string/action_download"
app:showAsAction="always" />
<item
android:id="@+id/action_dms"
android:icon="@drawable/ic_send_24"
android:title="@string/action_dms"
android:titleCondensed="@string/action_dms"
app:showAsAction="ifRoom" />
</menu>

View File

@ -1,13 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/direct_messages_nav_graph"
app:startDestination="@id/directMessagesInboxFragment">
<fragment
android:id="@+id/directMessagesInboxFragment"
android:name="awais.instagrabber.fragments.directmessages.DirectMessageInboxFragment"
android:label="DirectMessagesInboxFragment" >
android:label="@string/action_dms"
tools:layout="@layout/fragment_direct_messages_inbox">
<action
android:id="@+id/action_dMInboxFragment_to_dMThreadFragment"
app:destination="@id/directMessagesThreadFragment" />
@ -15,7 +17,8 @@
<fragment
android:id="@+id/directMessagesThreadFragment"
android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment"
android:label="DirectMessagesThreadFragment">
android:label="DirectMessagesThreadFragment"
tools:layout="@layout/fragment_direct_messages_thread">
<argument
android:name="threadId"
app:argType="string" />
@ -29,7 +32,8 @@
<fragment
android:id="@+id/directMessagesSettingsFragment"
android:name="awais.instagrabber.fragments.directmessages.DirectMessageSettingsFragment"
android:label="DirectMessagesSettingsFragment">
android:label="DirectMessagesSettingsFragment"
tools:layout="@layout/fragment_direct_messages_settings">
<argument
android:name="threadId"
app:argType="string" />

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/discover_nav_graph"
app:startDestination="@id/discoverFragment">
<fragment
android:id="@+id/discoverFragment"
android:name="awais.instagrabber.fragments.main.DiscoverFragment"
android:label="DiscoverFragment" />
</navigation>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/feed_nav_graph"
app:startDestination="@id/feedFragment">
<fragment
android:id="@+id/feedFragment"
android:name="awais.instagrabber.fragments.main.FeedFragment"
android:label="FeedFragment"
tools:layout="@layout/fragment_feed">
<action
android:id="@+id/action_feedFragment_to_storyViewerFragment"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_feedFragment_to_profileFragment"
app:destination="@id/profileFragment" />
</fragment>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="StoryViewerFragment"
tools:layout="@layout/activity_story_viewer">
<argument
android:name="feedStoryIndex"
app:argType="integer"
app:nullable="false" />
<argument
android:name="highlight"
app:argType="string"
app:nullable="true" />
<argument
android:name="isHashtag"
app:argType="boolean" />
</fragment>
<fragment
android:id="@+id/profileFragment"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="ProfileFragment"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
</fragment>
</navigation>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/profile_nav_graph"
app:startDestination="@id/profileFragment">
<fragment
android:id="@+id/profileFragment"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile" />
</navigation>

View File

@ -19,4 +19,6 @@
<dimen name="message_item_profile_size">@dimen/feed_profile_size</dimen>
<dimen name="dm_media_img_max_height">500dp</dimen>
<dimen name="dm_message_card_radius">8dp</dimen>
<dimen name="feed_item_bottom_icon_size">32dp</dimen>
</resources>

View File

@ -234,4 +234,9 @@
<string name="activity_count_usertags">%d usertags</string>
<string name="activity_count_likes">%d likes</string>
<string name="activity_notloggedin">You logged out before clicking this notification?!</string>
<string name="feed">Feed</string>
<string name="profile">Profile</string>
<string name="more">More</string>
<string name="title_dm">DM</string>
<string name="number_selected">%d selected</string>
</resources>

View File

@ -1,11 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="bottomSheetDialogTheme">@style/AppTheme.BottomSheetDialog</item>
<item name="android:windowAnimationStyle">@style/AppTheme.WindowAnimationTransition</item>
</style>
<style name="CompletelyTransparent" parent="Theme.AppCompat.NoActionBar">
<item name="android:background">@null</item>
<item name="android:backgroundDimEnabled">false</item>
@ -34,4 +29,16 @@
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
</style>
<style name="AppTheme.BottomNavigationView" parent="@style/Widget.MaterialComponents.BottomNavigationView.PrimarySurface">
<item name="materialThemeOverlay">@style/ThemeOverlay.App.BottomNavigationView</item>
</style>
<style name="ThemeOverlay.App.BottomNavigationView" parent="" />
<style name="Widget.App.ActionMode" parent="Widget.AppCompat.ActionMode">
<item name="titleTextStyle">?attr/textAppearanceHeadline6</item>
<item name="subtitleTextStyle">?attr/textAppearanceSubtitle1</item>
<item name="background">?attr/colorPrimary</item>
</style>
</resources>

Some files were not shown because too many files have changed in this diff Show More