Ammar Githam
4 years ago
9 changed files with 422 additions and 78 deletions
-
3app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
18app/src/main/java/awais/instagrabber/activities/SavedViewer.java
-
310app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
3app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
-
57app/src/main/java/awais/instagrabber/fragments/main/ProfileActionListener.java
-
17app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/main/viewmodels/PostsViewModel.java
-
77app/src/main/res/layout/fragment_hashtag.xml
-
13app/src/main/res/navigation/feed_nav_graph.xml
@ -0,0 +1,310 @@ |
|||
package awais.instagrabber.fragments; |
|||
|
|||
import android.content.Intent; |
|||
import android.content.res.ColorStateList; |
|||
import android.graphics.Typeface; |
|||
import android.os.AsyncTask; |
|||
import android.os.Bundle; |
|||
import android.text.SpannableStringBuilder; |
|||
import android.text.style.RelativeSizeSpan; |
|||
import android.text.style.StyleSpan; |
|||
import android.util.Log; |
|||
import android.view.ActionMode; |
|||
import android.view.LayoutInflater; |
|||
import android.view.MenuItem; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
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.core.content.ContextCompat; |
|||
import androidx.core.view.ViewCompat; |
|||
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.PostsAdapter; |
|||
import awais.instagrabber.asyncs.HashtagFetcher; |
|||
import awais.instagrabber.asyncs.PostsFetcher; |
|||
import awais.instagrabber.asyncs.i.iStoryStatusFetcher; |
|||
import awais.instagrabber.customviews.PrimaryActionModeCallback; |
|||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager; |
|||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; |
|||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; |
|||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
|||
import awais.instagrabber.databinding.FragmentHashtagBinding; |
|||
import awais.instagrabber.fragments.main.viewmodels.PostsViewModel; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.models.HashtagModel; |
|||
import awais.instagrabber.models.PostModel; |
|||
import awais.instagrabber.models.StoryModel; |
|||
import awais.instagrabber.models.enums.DownloadMethod; |
|||
import awais.instagrabber.models.enums.ItemGetType; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.Utils; |
|||
import awaisomereport.LogCollector; |
|||
|
|||
import static awais.instagrabber.utils.Utils.logCollector; |
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public class HashTagFragment extends Fragment { |
|||
private static final String TAG = "HashTagFragment"; |
|||
|
|||
private MainActivity fragmentActivity; |
|||
private FragmentHashtagBinding binding; |
|||
private NestedCoordinatorLayout root; |
|||
private boolean shouldRefresh = true; |
|||
private String hashtag; |
|||
private HashtagModel hashtagModel; |
|||
private PostsViewModel postsViewModel; |
|||
private PostsAdapter postsAdapter; |
|||
private ActionMode actionMode; |
|||
private boolean hasNextPage; |
|||
private String endCursor; |
|||
private AsyncTask<?, ?, ?> currentlyExecuting; |
|||
private boolean isLoggedIn; |
|||
private StoryModel[] storyModels; |
|||
|
|||
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { |
|||
@Override |
|||
public void handleOnBackPressed() { |
|||
if (postsAdapter == null) { |
|||
setEnabled(false); |
|||
remove(); |
|||
return; |
|||
} |
|||
postsAdapter.clearSelection(); |
|||
setEnabled(false); |
|||
remove(); |
|||
} |
|||
}; |
|||
private final PrimaryActionModeCallback multiSelectAction = new PrimaryActionModeCallback( |
|||
R.menu.multi_select_download_menu, |
|||
new PrimaryActionModeCallback.CallbacksHelper() { |
|||
@Override |
|||
public void onDestroy(final ActionMode mode) { |
|||
onBackPressedCallback.handleOnBackPressed(); |
|||
} |
|||
|
|||
@Override |
|||
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { |
|||
if (item.getItemId() == R.id.action_download) { |
|||
if (postsAdapter == null || hashtag == null) { |
|||
return false; |
|||
} |
|||
Utils.batchDownload(requireContext(), |
|||
hashtag, |
|||
DownloadMethod.DOWNLOAD_MAIN, |
|||
postsAdapter.getSelectedModels()); |
|||
checkAndResetAction(); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
}); |
|||
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() { |
|||
@Override |
|||
public void onResult(final PostModel[] result) { |
|||
binding.swipeRefreshLayout.setRefreshing(false); |
|||
if (result == null) return; |
|||
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE)); |
|||
final List<PostModel> postModels = postsViewModel.getList().getValue(); |
|||
final List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>() : new ArrayList<>(postModels); |
|||
finalList.addAll(Arrays.asList(result)); |
|||
postsViewModel.getList().postValue(finalList); |
|||
PostModel model = null; |
|||
if (result.length != 0) { |
|||
model = result[result.length - 1]; |
|||
} |
|||
if (model == null) return; |
|||
endCursor = model.getEndCursor(); |
|||
hasNextPage = model.hasNextPage(); |
|||
model.setPageCursor(false, null); |
|||
} |
|||
}; |
|||
|
|||
@Override |
|||
public void onCreate(@Nullable final Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
fragmentActivity = (MainActivity) requireActivity(); |
|||
} |
|||
|
|||
@Nullable |
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { |
|||
if (root != null) { |
|||
shouldRefresh = false; |
|||
return root; |
|||
} |
|||
binding = FragmentHashtagBinding.inflate(inflater, container, false); |
|||
root = binding.getRoot(); |
|||
return root; |
|||
} |
|||
|
|||
@Override |
|||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
|||
if (!shouldRefresh) return; |
|||
init(); |
|||
shouldRefresh = false; |
|||
} |
|||
|
|||
@Override |
|||
public void onDestroy() { |
|||
super.onDestroy(); |
|||
if (postsViewModel != null) { |
|||
postsViewModel.getList().postValue(Collections.emptyList()); |
|||
} |
|||
} |
|||
|
|||
private void init() { |
|||
if (getArguments() == null) return; |
|||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|||
isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null; |
|||
final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments()); |
|||
hashtag = fragmentArgs.getHashtag(); |
|||
setTitle(); |
|||
setupPosts(); |
|||
fetchHashtagModel(); |
|||
} |
|||
|
|||
private void setupPosts() { |
|||
postsViewModel = new ViewModelProvider(this).get(PostsViewModel.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, hashtag) |
|||
.putExtra(Constants.EXTRAS_TYPE, ItemGetType.MAIN_ITEMS)); |
|||
|
|||
}, (model, position) -> { |
|||
if (!postsAdapter.isSelecting()) { |
|||
checkAndResetAction(); |
|||
return true; |
|||
} |
|||
if (onBackPressedCallback.isEnabled()) { |
|||
return true; |
|||
} |
|||
final OnBackPressedDispatcher onBackPressedDispatcher = fragmentActivity.getOnBackPressedDispatcher(); |
|||
onBackPressedCallback.setEnabled(true); |
|||
actionMode = fragmentActivity.startActionMode(multiSelectAction); |
|||
final String title = getString(R.string.number_selected, 1); |
|||
actionMode.setTitle(title); |
|||
onBackPressedDispatcher.addCallback(getViewLifecycleOwner(), onBackPressedCallback); |
|||
return true; |
|||
}); |
|||
postsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList); |
|||
binding.mainPosts.setAdapter(postsAdapter); |
|||
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|||
if (!hasNextPage) return; |
|||
binding.swipeRefreshLayout.setRefreshing(true); |
|||
fetchPosts(); |
|||
endCursor = null; |
|||
}); |
|||
binding.mainPosts.addOnScrollListener(lazyLoader); |
|||
} |
|||
|
|||
private void fetchHashtagModel() { |
|||
stopCurrentExecutor(); |
|||
binding.swipeRefreshLayout.setRefreshing(true); |
|||
currentlyExecuting = new HashtagFetcher(hashtag.substring(1), result -> { |
|||
hashtagModel = result; |
|||
binding.swipeRefreshLayout.setRefreshing(false); |
|||
if (hashtagModel == null) { |
|||
Toast.makeText(requireContext(), R.string.error_loading_profile, Toast.LENGTH_SHORT).show(); |
|||
return; |
|||
} |
|||
fetchPosts(); |
|||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} |
|||
|
|||
private void fetchPosts() { |
|||
stopCurrentExecutor(); |
|||
binding.btnFollowTag.setVisibility(View.VISIBLE); |
|||
binding.swipeRefreshLayout.setRefreshing(true); |
|||
currentlyExecuting = new PostsFetcher(hashtag, endCursor, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
if (isLoggedIn) { |
|||
new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, false, false, stories -> { |
|||
storyModels = stories; |
|||
if (stories != null && stories.length > 0) { |
|||
binding.mainHashtagImage.setStoriesBorder(); |
|||
} |
|||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
|
|||
binding.btnFollowTag.setText(hashtagModel.getFollowing() ? R.string.unfollow : R.string.follow); |
|||
ViewCompat.setBackgroundTintList(binding.btnFollowTag, ColorStateList.valueOf( |
|||
ContextCompat.getColor(requireContext(), hashtagModel.getFollowing() |
|||
? R.color.btn_purple_background |
|||
: R.color.btn_pink_background))); |
|||
} else { |
|||
binding.btnFollowTag.setText(Utils.dataBox.getFavorite(hashtag) != null |
|||
? R.string.unfavorite_short |
|||
: R.string.favorite_short); |
|||
ViewCompat.setBackgroundTintList(binding.btnFollowTag, ColorStateList.valueOf( |
|||
ContextCompat.getColor(requireContext(), Utils.dataBox.getFavorite(hashtag) != null |
|||
? R.color.btn_purple_background |
|||
: R.color.btn_pink_background))); |
|||
} |
|||
binding.mainHashtagImage.setImageURI(hashtagModel.getSdProfilePic()); |
|||
final String postCount = String.valueOf(hashtagModel.getPostCount()); |
|||
final 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.mainTagPostCount.setText(span); |
|||
binding.mainTagPostCount.setVisibility(View.VISIBLE); |
|||
} |
|||
|
|||
public void stopCurrentExecutor() { |
|||
if (currentlyExecuting != null) { |
|||
try { |
|||
currentlyExecuting.cancel(true); |
|||
} catch (final Exception e) { |
|||
if (logCollector != null) |
|||
logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor"); |
|||
Log.e(TAG, "", e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void setTitle() { |
|||
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); |
|||
if (actionBar != null) { |
|||
actionBar.setTitle(hashtag.substring(1)); |
|||
} |
|||
} |
|||
|
|||
private boolean checkAndResetAction() { |
|||
if (!onBackPressedCallback.isEnabled() && actionMode == null) { |
|||
return false; |
|||
} |
|||
if (onBackPressedCallback.isEnabled()) { |
|||
onBackPressedCallback.setEnabled(false); |
|||
onBackPressedCallback.remove(); |
|||
} |
|||
if (actionMode != null) { |
|||
actionMode.finish(); |
|||
actionMode = null; |
|||
} |
|||
return true; |
|||
} |
|||
} |
@ -1,57 +0,0 @@ |
|||
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,77 @@ |
|||
<?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"> |
|||
|
|||
<LinearLayout |
|||
android:id="@+id/tagInfoContainer" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:orientation="horizontal" |
|||
android:padding="@dimen/profile_info_container_bottom_space" |
|||
android:visibility="visible"> |
|||
|
|||
<awais.instagrabber.customviews.CircularImageView |
|||
android:id="@+id/mainHashtagImage" |
|||
android:layout_width="@dimen/profile_picture_size" |
|||
android:layout_height="@dimen/profile_picture_size" |
|||
android:adjustViewBounds="true" |
|||
android:background="?selectableItemBackgroundBorderless" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/mainTagPostCount" |
|||
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.AppCompatButton |
|||
android:id="@+id/btnFollowTag" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center_vertical" |
|||
android:layout_weight="2" |
|||
android:text="@string/follow" |
|||
android:textColor="@color/btn_pink_text_color" |
|||
android:textSize="20sp" |
|||
android:visibility="gone" |
|||
app:backgroundTint="@color/btn_pink_background" /> |
|||
</LinearLayout> |
|||
</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> |
|||
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue