-
29app/build.gradle
-
234app/src/main/java/awais/instagrabber/MainHelper.java
-
633app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
607app/src/main/java/awais/instagrabber/activities/MainActivityBackup.java
-
10app/src/main/java/awais/instagrabber/activities/PostViewer.java
-
78app/src/main/java/awais/instagrabber/activities/ProfileViewer.java
-
172app/src/main/java/awais/instagrabber/activities/SavedViewer.java
-
936app/src/main/java/awais/instagrabber/activities/StoryViewer.java
-
77app/src/main/java/awais/instagrabber/adapters/DiscoverAdapter.java
-
13app/src/main/java/awais/instagrabber/adapters/FeedAdapter.java
-
58app/src/main/java/awais/instagrabber/adapters/FeedStoriesAdapter.java
-
17app/src/main/java/awais/instagrabber/adapters/HighlightsAdapter.java
-
106app/src/main/java/awais/instagrabber/adapters/MultiSelectListAdapter.java
-
89app/src/main/java/awais/instagrabber/adapters/PostsAdapter.java
-
104app/src/main/java/awais/instagrabber/adapters/StoriesAdapter.java
-
10app/src/main/java/awais/instagrabber/adapters/viewholder/DiscoverViewHolder.java
-
37app/src/main/java/awais/instagrabber/adapters/viewholder/HighlightViewHolder.java
-
42app/src/main/java/awais/instagrabber/adapters/viewholder/PostViewHolder.java
-
7app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedItemViewHolder.java
-
16app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedPhotoViewHolder.java
-
4app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedSliderViewHolder.java
-
7app/src/main/java/awais/instagrabber/adapters/viewholder/feed/FeedVideoViewHolder.java
-
81app/src/main/java/awais/instagrabber/asyncs/CommentAction.java
-
18app/src/main/java/awais/instagrabber/asyncs/PostsFetcher.java
-
88app/src/main/java/awais/instagrabber/asyncs/QuizAction.java
-
87app/src/main/java/awais/instagrabber/asyncs/RespondAction.java
-
51app/src/main/java/awais/instagrabber/asyncs/SeenAction.java
-
69app/src/main/java/awais/instagrabber/asyncs/VoteAction.java
-
59app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java
-
71app/src/main/java/awais/instagrabber/customviews/PrimaryActionModeCallback.java
-
2app/src/main/java/awais/instagrabber/customviews/drawee/ZoomableDraweeView.java
-
33app/src/main/java/awais/instagrabber/customviews/helpers/CustomHideBottomViewOnScrollBehavior.java
-
154app/src/main/java/awais/instagrabber/customviews/helpers/NestedCoordinatorLayout.java
-
5app/src/main/java/awais/instagrabber/dialogs/QuickAccessDialog.java
-
46app/src/main/java/awais/instagrabber/directdownload/MultiDirectDialog.java
-
673app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
-
12app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
31app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
241app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java
-
345app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
-
57app/src/main/java/awais/instagrabber/fragments/main/ProfileActionListener.java
-
532app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
-
19app/src/main/java/awais/instagrabber/fragments/main/viewmodels/DiscoverItemViewModel.java
-
19app/src/main/java/awais/instagrabber/fragments/main/viewmodels/FeedStoriesViewModel.java
-
20app/src/main/java/awais/instagrabber/fragments/main/viewmodels/FeedViewModel.java
-
19app/src/main/java/awais/instagrabber/fragments/main/viewmodels/ProfilePostsViewModel.java
-
19app/src/main/java/awais/instagrabber/fragments/main/viewmodels/StoriesViewModel.java
-
17app/src/main/java/awais/instagrabber/models/BasePostModel.java
-
2app/src/main/java/awais/instagrabber/models/PostModel.java
-
4app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java
-
18app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java
-
24app/src/main/java/awais/instagrabber/services/AddCookiesInterceptor.java
-
24app/src/main/java/awais/instagrabber/services/BaseService.java
-
4app/src/main/java/awais/instagrabber/services/FeedService.java
-
35app/src/main/java/awais/instagrabber/services/ProfileService.java
-
7app/src/main/java/awais/instagrabber/services/ServiceCallback.java
-
277app/src/main/java/awais/instagrabber/services/StoriesService.java
-
207app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java
-
24app/src/main/java/awais/instagrabber/utils/Utils.java
-
BINapp/src/main/res/drawable-night/comments.png
-
BINapp/src/main/res/drawable-night/mute.png
-
BINapp/src/main/res/drawable-night/video_views.png
-
BINapp/src/main/res/drawable-night/vol.png
-
BINapp/src/main/res/drawable/comments.png
-
10app/src/main/res/drawable/ic_close_24.xml
-
10app/src/main/res/drawable/ic_more_horiz_24.xml
-
10app/src/main/res/drawable/ic_open_in_new_24.xml
-
10app/src/main/res/drawable/ic_outline_comments_24.xml
-
10app/src/main/res/drawable/ic_outline_views_24.xml
-
9app/src/main/res/drawable/ic_star_24.xml
-
10app/src/main/res/drawable/ic_volume_off_24.xml
-
10app/src/main/res/drawable/ic_volume_up_24.xml
-
BINapp/src/main/res/drawable/mute.png
-
BINapp/src/main/res/drawable/video_views.png
-
BINapp/src/main/res/drawable/vol.png
-
107app/src/main/res/layout/activity_main.xml
-
104app/src/main/res/layout/activity_mainbackup.xml
-
41app/src/main/res/layout/activity_story_viewer.xml
-
19app/src/main/res/layout/fragment_direct_messages_inbox.xml
-
4app/src/main/res/layout/fragment_direct_messages_thread.xml
-
43app/src/main/res/layout/fragment_discover.xml
-
41app/src/main/res/layout/fragment_feed.xml
-
304app/src/main/res/layout/fragment_profile.xml
-
116app/src/main/res/layout/item_feed_bottom.xml
-
5app/src/main/res/layout/item_feed_photo.xml
-
28app/src/main/res/layout/item_feed_top.xml
-
13app/src/main/res/layout/item_highlight.xml
-
22app/src/main/res/layout/item_post.xml
-
16app/src/main/res/layout/item_story.xml
-
30app/src/main/res/menu/main_bottom_navigation_menu.xml
-
18app/src/main/res/menu/main_menu.xml
-
11app/src/main/res/menu/multi_select_download_menu.xml
-
17app/src/main/res/menu/story_menu.xml
-
10app/src/main/res/navigation/direct_messages_nav_graph.xml
-
11app/src/main/res/navigation/discover_nav_graph.xml
-
47app/src/main/res/navigation/feed_nav_graph.xml
-
12app/src/main/res/navigation/profile_nav_graph.xml
-
2app/src/main/res/values/dimens.xml
-
5app/src/main/res/values/strings.xml
-
17app/src/main/res/values/styles.xml
@ -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<>(); |
|||
private ActivityMainBinding binding; |
|||
private LiveData<NavController> currentNavControllerLiveData; |
|||
|
|||
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; |
|||
|
|||
@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; |
|||
|
|||
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()); |
|||
final Toolbar toolbar = binding.toolbar; |
|||
setSupportActionBar(toolbar); |
|||
|
|||
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(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); |
|||
}); |
|||
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); |
|||
} |
|||
if (savedInstanceState == null) { |
|||
setupBottomNavigationBar(); |
|||
} |
|||
|
|||
@Override |
|||
protected void onNewIntent(final Intent intent) { |
|||
super.onNewIntent(intent); |
|||
mainHelper.onIntent(intent); |
|||
setupScrollingListener(); |
|||
} |
|||
|
|||
@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); |
|||
private void setupScrollingListener() { |
|||
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.bottomNavView.getLayoutParams(); |
|||
layoutParams.setBehavior(new CustomHideBottomViewOnScrollBehavior()); |
|||
binding.bottomNavView.requestLayout(); |
|||
} |
|||
|
|||
@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); |
|||
} |
|||
} |
|||
getMenuInflater().inflate(R.menu.main_menu, menu); |
|||
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); |
|||
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 |
|||
)); |
|||
|
|||
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; |
|||
} |
|||
|
|||
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); |
|||
|
|||
if (showBottomView.contains(destinationId)) { |
|||
setScrollingBehaviour(); |
|||
binding.bottomNavView.setVisibility(View.VISIBLE); |
|||
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; |
|||
removeScrollingBehaviour(); |
|||
binding.bottomNavView.setVisibility(View.GONE); |
|||
}); |
|||
} |
|||
|
|||
if (queriesStack != null && queriesStack.size() > 0) { |
|||
userQuery = queriesStack.pop(); |
|||
if (userQuery != null) { |
|||
mainHelper.onRefresh(); |
|||
return; |
|||
} |
|||
} else { |
|||
finish(); |
|||
} |
|||
private void setScrollingBehaviour() { |
|||
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.mainNavHost.getLayoutParams(); |
|||
layoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); |
|||
binding.mainNavHost.requestLayout(); |
|||
} |
|||
|
|||
@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(); |
|||
private void removeScrollingBehaviour() { |
|||
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.mainNavHost.getLayoutParams(); |
|||
layoutParams.setBehavior(null); |
|||
binding.mainNavHost.requestLayout(); |
|||
} |
|||
|
|||
@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(); |
|||
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) { |
|||
super.onRestoreInstanceState(savedInstanceState); |
|||
setupBottomNavigationBar(); |
|||
} |
|||
|
|||
@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; |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
// } |
|||
// |
|||
// } |
@ -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> { |
|||
|
|||
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 ArrayList<DiscoverItemModel> discoverItemModels, final View.OnClickListener clickListener, |
|||
final View.OnLongClickListener longClickListener) { |
|||
this.discoverItemModels = discoverItemModels; |
|||
this.longClickListener = longClickListener; |
|||
this.clickListener = clickListener; |
|||
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; |
|||
holder.postImage.setImageURI(itemModel.getDisplayUrl()); |
|||
} |
|||
}).into(holder.postImage); |
|||
|
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public int getItemCount() { |
|||
return discoverItemModels == null ? 0 : discoverItemModels.size(); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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 PostsAdapter(final ArrayList<PostModel> postModels, final View.OnClickListener clickListener, |
|||
final View.OnLongClickListener longClickListener) { |
|||
this.postModels = postModels; |
|||
this.clickListener = clickListener; |
|||
this.longClickListener = longClickListener; |
|||
} |
|||
public final class PostsAdapter extends MultiSelectListAdapter<PostModel, PostViewHolder> { |
|||
|
|||
@NonNull |
|||
private static final DiffUtil.ItemCallback<PostModel> diffCallback = new DiffUtil.ItemCallback<PostModel>() { |
|||
@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)); |
|||
public boolean areItemsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) { |
|||
return oldItem.getPostId().equals(newItem.getPostId()); |
|||
} |
|||
|
|||
@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); |
|||
public boolean areContentsTheSame(@NonNull final PostModel oldItem, @NonNull final PostModel newItem) { |
|||
return oldItem.getPostId().equals(newItem.getPostId()); |
|||
} |
|||
}; |
|||
|
|||
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; |
|||
public PostsAdapter(final OnItemClickListener<PostModel> clickListener, |
|||
final OnItemLongClickListener<PostModel> longClickListener) { |
|||
super(diffCallback, clickListener, longClickListener); |
|||
} |
|||
|
|||
@NonNull |
|||
@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); |
|||
} |
|||
public PostViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
|||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
final ItemPostBinding binding = ItemPostBinding.inflate(layoutInflater, parent, false); |
|||
return new PostViewHolder(binding); |
|||
} |
|||
|
|||
@Override |
|||
public int getItemCount() { |
|||
return postModels == null ? 0 : postModels.size(); |
|||
public void onBindViewHolder(@NonNull final PostViewHolder holder, final int position) { |
|||
final PostModel postModel = getItem(position); |
|||
holder.bind(postModel, position, internalOnItemClickListener, internalOnLongItemClickListener); |
|||
} |
|||
} |
@ -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 StoriesAdapter(final StoryModel[] storyModels, final View.OnClickListener clickListener) { |
|||
this.storyModels = storyModels; |
|||
this.clickListener = clickListener; |
|||
} |
|||
public final class StoriesAdapter extends ListAdapter<StoryModel, StoriesAdapter.StoryViewHolder> { |
|||
private final OnItemClickListener onItemClickListener; |
|||
|
|||
@NonNull |
|||
private static final DiffUtil.ItemCallback<StoryModel> diffCallback = new DiffUtil.ItemCallback<StoryModel>() { |
|||
@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)); |
|||
public boolean areItemsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) { |
|||
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); |
|||
} |
|||
|
|||
@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 boolean areContentsTheSame(@NonNull final StoryModel oldItem, @NonNull final StoryModel newItem) { |
|||
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()); |
|||
} |
|||
}; |
|||
|
|||
public void setData(final StoryModel[] storyModels) { |
|||
this.storyModels = storyModels; |
|||
notifyDataSetChanged(); |
|||
public StoriesAdapter(final OnItemClickListener onItemClickListener) { |
|||
super(diffCallback); |
|||
this.onItemClickListener = onItemClickListener; |
|||
} |
|||
|
|||
public StoryModel getItemAt(final int position) { |
|||
return storyModels == null ? null : storyModels[position]; |
|||
@NonNull |
|||
@Override |
|||
public StoryViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
|||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
final ItemStoryBinding binding = ItemStoryBinding.inflate(layoutInflater, parent, false); |
|||
return new StoryViewHolder(binding); |
|||
} |
|||
|
|||
@Override |
|||
public int getItemCount() { |
|||
return storyModels == null ? 0 : storyModels.length; |
|||
public void onBindViewHolder(@NonNull final StoryViewHolder holder, final int position) { |
|||
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(final ItemStoryBinding binding) { |
|||
super(binding.getRoot()); |
|||
this.binding = binding; |
|||
} |
|||
|
|||
public StoryViewHolder(@NonNull final View itemView) { |
|||
super(itemView); |
|||
selectedView = itemView.findViewById(R.id.selectedView); |
|||
icon = itemView.findViewById(R.id.icon); |
|||
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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
|
|||
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); |
|||
private final ItemPostBinding binding; |
|||
|
|||
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()); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
}); |
|||
} |
|||
} |
@ -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) { |
|||
// |
|||
// } |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
package awais.instagrabber.repositories; |
|||
|
|||
public interface ProfileRepository { |
|||
} |
@ -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); |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
package awais.instagrabber.services; |
|||
|
|||
public interface FeedService { |
|||
} |
@ -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/")); |
|||
} |
|||
} |
@ -0,0 +1,7 @@ |
|||
package awais.instagrabber.services; |
|||
|
|||
public interface ServiceCallback<T> { |
|||
void onSuccess(T result); |
|||
|
|||
void onFailure(Throwable t); |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
Before Width: 200 | Height: 200 | Size: 6.1 KiB |
Before Width: 200 | Height: 200 | Size: 6.8 KiB |
Before Width: 200 | Height: 200 | Size: 10 KiB |
Before Width: 200 | Height: 200 | Size: 7.3 KiB |
Before Width: 200 | Height: 200 | Size: 4.6 KiB |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
Before Width: 200 | Height: 200 | Size: 5.3 KiB |
Before Width: 200 | Height: 200 | Size: 6.4 KiB |
Before Width: 200 | Height: 200 | Size: 5.7 KiB |
@ -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" |
|||
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"> |
|||
tools:context=".activities.MainActivity"> |
|||
|
|||
<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> |
@ -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> |
@ -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" /> |
|||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
|||
android:layout_height="match_parent" |
|||
tools:listitem="@layout/layout_include_simple_item" /> |
|||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
|||
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |