-
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
-
8app/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
-
51app/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
-
17app/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; |
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.Bundle; |
||||
import android.os.Handler; |
|
||||
import android.os.PersistableBundle; |
|
||||
import android.provider.BaseColumns; |
|
||||
import android.text.TextUtils; |
|
||||
import android.view.Menu; |
import android.view.Menu; |
||||
import android.view.MenuItem; |
|
||||
import android.view.View; |
import android.view.View; |
||||
import android.widget.ArrayAdapter; |
|
||||
import android.widget.AutoCompleteTextView; |
|
||||
import android.widget.Toast; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
import androidx.annotation.NonNull; |
||||
import androidx.annotation.Nullable; |
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.ArrayList; |
||||
|
import java.util.Arrays; |
||||
import java.util.List; |
import java.util.List; |
||||
import java.util.Stack; |
|
||||
|
|
||||
import awais.instagrabber.MainHelper; |
|
||||
import awais.instagrabber.R; |
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.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.Constants; |
||||
import awais.instagrabber.utils.DataBox; |
|
||||
import awais.instagrabber.utils.FlavorTown; |
|
||||
import awais.instagrabber.utils.Utils; |
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; |
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 |
@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); |
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 |
@Override |
||||
public boolean onCreateOptionsMenu(final Menu menu) { |
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; |
||||
} |
} |
||||
}); |
|
||||
|
|
||||
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; |
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 |
@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 |
@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; |
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; |
package awais.instagrabber.adapters; |
||||
|
|
||||
import android.graphics.drawable.Drawable; |
|
||||
import android.view.LayoutInflater; |
import android.view.LayoutInflater; |
||||
import android.view.View; |
import android.view.View; |
||||
import android.view.ViewGroup; |
import android.view.ViewGroup; |
||||
|
|
||||
import androidx.annotation.NonNull; |
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.R; |
||||
import awais.instagrabber.adapters.viewholder.DiscoverViewHolder; |
import awais.instagrabber.adapters.viewholder.DiscoverViewHolder; |
||||
import awais.instagrabber.models.DiscoverItemModel; |
import awais.instagrabber.models.DiscoverItemModel; |
||||
import awais.instagrabber.models.enums.MediaItemType; |
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 |
@NonNull |
||||
@Override |
@Override |
||||
public DiscoverViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
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)); |
return new DiscoverViewHolder(layoutInflater.inflate(R.layout.item_post, parent, false)); |
||||
} |
} |
||||
|
|
||||
@Override |
@Override |
||||
public void onBindViewHolder(@NonNull final DiscoverViewHolder holder, final int position) { |
public void onBindViewHolder(@NonNull final DiscoverViewHolder holder, final int position) { |
||||
final DiscoverItemModel itemModel = discoverItemModels.get(position); |
|
||||
|
final DiscoverItemModel itemModel = getItem(position); |
||||
if (itemModel != null) { |
if (itemModel != null) { |
||||
itemModel.setPosition(position); |
itemModel.setPosition(position); |
||||
holder.itemView.setTag(itemModel); |
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(); |
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.typeIcon.setImageResource(mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? R.drawable.slider : R.drawable.video); |
||||
|
|
||||
holder.selectedView.setVisibility(itemModel.isSelected() ? View.VISIBLE : View.GONE); |
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; |
package awais.instagrabber.adapters; |
||||
|
|
||||
import android.view.LayoutInflater; |
import android.view.LayoutInflater; |
||||
import android.view.View; |
|
||||
import android.view.ViewGroup; |
import android.view.ViewGroup; |
||||
|
|
||||
import androidx.annotation.NonNull; |
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.adapters.viewholder.HighlightViewHolder; |
||||
|
import awais.instagrabber.databinding.ItemHighlightBinding; |
||||
import awais.instagrabber.models.FeedStoryModel; |
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 |
@NonNull |
||||
@Override |
@Override |
||||
public HighlightViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
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 |
@Override |
||||
public void onBindViewHolder(@NonNull final HighlightViewHolder holder, final int position) { |
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; |
package awais.instagrabber.adapters; |
||||
|
|
||||
import android.graphics.drawable.Drawable; |
|
||||
import android.view.LayoutInflater; |
import android.view.LayoutInflater; |
||||
import android.view.View; |
|
||||
import android.view.ViewGroup; |
import android.view.ViewGroup; |
||||
|
|
||||
import androidx.annotation.NonNull; |
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.adapters.viewholder.PostViewHolder; |
||||
|
import awais.instagrabber.databinding.ItemPostBinding; |
||||
import awais.instagrabber.models.PostModel; |
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 |
@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 |
@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 |
@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 |
@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; |
package awais.instagrabber.adapters; |
||||
|
|
||||
import android.content.Context; |
|
||||
import android.content.res.Resources; |
|
||||
import android.view.LayoutInflater; |
import android.view.LayoutInflater; |
||||
import android.view.View; |
import android.view.View; |
||||
import android.view.ViewGroup; |
import android.view.ViewGroup; |
||||
import android.widget.ImageView; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
import androidx.annotation.NonNull; |
||||
|
import androidx.recyclerview.widget.DiffUtil; |
||||
|
import androidx.recyclerview.widget.ListAdapter; |
||||
import androidx.recyclerview.widget.RecyclerView; |
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; |
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 |
@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 |
@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 |
@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 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; |
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 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 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; |
package awais.instagrabber.adapters.viewholder; |
||||
|
|
||||
import android.view.View; |
import android.view.View; |
||||
import android.widget.ImageView; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
import androidx.annotation.NonNull; |
||||
import androidx.recyclerview.widget.RecyclerView; |
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
import awais.instagrabber.R; |
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 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"?> |
<?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:app="http://schemas.android.com/apk/res-auto" |
||||
xmlns:tools="http://schemas.android.com/tools" |
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:id="@+id/main_container" |
||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||
android:layout_height="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:animateLayoutChanges="true" |
||||
android:tag="@android:string/yes"> |
|
||||
|
tools:context=".activities.MainActivity"> |
||||
|
|
||||
<com.google.android.material.appbar.AppBarLayout |
<com.google.android.material.appbar.AppBarLayout |
||||
|
android:id="@+id/appBarLayout" |
||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
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 |
<com.google.android.material.appbar.CollapsingToolbarLayout |
||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
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_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.CollapsingToolbarLayout> |
||||
</com.google.android.material.appbar.AppBarLayout> |
</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_width="match_parent" |
||||
android:layout_height="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_width="match_parent" |
||||
android:layout_height="wrap_content" |
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"?> |
<?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_width="match_parent" |
||||
android:layout_height="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 |
<androidx.recyclerview.widget.RecyclerView |
||||
android:id="@+id/inbox_list" |
android:id="@+id/inbox_list" |
||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" /> |
|
||||
|
android:layout_height="match_parent" |
||||
|
tools:listitem="@layout/layout_include_simple_item" /> |
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
</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> |