saved viewer
@ -177,6 +177,15 @@
 | 
			
		||||
                android:value=".activities.Main" />
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activities.SavedViewer"
 | 
			
		||||
            android:parentActivityName=".activities.Main">
 | 
			
		||||
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="android.support.PARENT_ACTIVITY"
 | 
			
		||||
                android:value=".activities.Main" />
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".activities.Login"
 | 
			
		||||
            android:label="@string/login"
 | 
			
		||||
 | 
			
		||||
@ -46,6 +46,7 @@ import java.util.Arrays;
 | 
			
		||||
import awais.instagrabber.activities.FollowViewer;
 | 
			
		||||
import awais.instagrabber.activities.Main;
 | 
			
		||||
import awais.instagrabber.activities.PostViewer;
 | 
			
		||||
import awais.instagrabber.activities.SavedViewer;
 | 
			
		||||
import awais.instagrabber.activities.StoryViewer;
 | 
			
		||||
import awais.instagrabber.adapters.DiscoverAdapter;
 | 
			
		||||
import awais.instagrabber.adapters.FeedAdapter;
 | 
			
		||||
@ -522,7 +523,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupExplore() {
 | 
			
		||||
        final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(main, Utils.convertDpToPx(130));
 | 
			
		||||
        final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(main, Utils.convertDpToPx(110));
 | 
			
		||||
        main.mainBinding.discoverPosts.setLayoutManager(layoutManager);
 | 
			
		||||
        main.mainBinding.discoverPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
 | 
			
		||||
 | 
			
		||||
@ -650,8 +651,17 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
        main.mainBinding.btnFollow.setVisibility(View.GONE);
 | 
			
		||||
        main.mainBinding.btnRestrict.setVisibility(View.GONE);
 | 
			
		||||
        main.mainBinding.btnBlock.setVisibility(View.GONE);
 | 
			
		||||
        main.mainBinding.btnSaved.setVisibility(View.GONE);
 | 
			
		||||
        main.mainBinding.btnTagged.setVisibility(View.GONE);
 | 
			
		||||
        main.mainBinding.btnMap.setVisibility(View.GONE);
 | 
			
		||||
 | 
			
		||||
        main.mainBinding.btnFollow.setOnClickListener(profileActionListener);
 | 
			
		||||
        main.mainBinding.btnRestrict.setOnClickListener(profileActionListener);
 | 
			
		||||
        main.mainBinding.btnBlock.setOnClickListener(profileActionListener);
 | 
			
		||||
        main.mainBinding.btnSaved.setOnClickListener(profileActionListener);
 | 
			
		||||
        main.mainBinding.btnTagged.setOnClickListener(profileActionListener);
 | 
			
		||||
        main.mainBinding.btnFollowTag.setOnClickListener(profileActionListener);
 | 
			
		||||
 | 
			
		||||
        main.mainBinding.infoContainer.setVisibility(View.GONE);
 | 
			
		||||
        main.mainBinding.tagInfoContainer.setVisibility(View.GONE);
 | 
			
		||||
        main.mainBinding.locInfoContainer.setVisibility(View.GONE);
 | 
			
		||||
@ -698,7 +708,6 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
                        .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
 | 
			
		||||
                main.mainBinding.btnFollowTag.setVisibility(View.VISIBLE);
 | 
			
		||||
                main.mainBinding.btnFollowTag.setOnClickListener(profileActionListener);
 | 
			
		||||
 | 
			
		||||
                if (isLoggedIn) {
 | 
			
		||||
                    new StoryStatusFetcher(profileId, hashtagModel.getName(), false, result -> {
 | 
			
		||||
@ -779,8 +788,9 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
 | 
			
		||||
                    final String myId = Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE));
 | 
			
		||||
                    if (!profileId.equals(myId)) {
 | 
			
		||||
                        main.mainBinding.btnTagged.setVisibility(View.GONE);
 | 
			
		||||
                        main.mainBinding.btnSaved.setVisibility(View.GONE);
 | 
			
		||||
                        main.mainBinding.btnFollow.setVisibility(View.VISIBLE);
 | 
			
		||||
                        main.mainBinding.btnFollow.setOnClickListener(profileActionListener);
 | 
			
		||||
                        if (profileModel.getFollowing() == true) {
 | 
			
		||||
                            main.mainBinding.btnFollow.setText(R.string.unfollow);
 | 
			
		||||
                            main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
@ -797,30 +807,51 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
                                    R.color.btn_pink_background, null)));
 | 
			
		||||
                        }
 | 
			
		||||
                        main.mainBinding.btnRestrict.setVisibility(View.VISIBLE);
 | 
			
		||||
                        main.mainBinding.btnRestrict.setOnClickListener(profileActionListener);
 | 
			
		||||
                        if (profileModel.getRestricted() == true) {
 | 
			
		||||
                            main.mainBinding.btnRestrict.setText(R.string.unrestrict);
 | 
			
		||||
                            main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                    R.color.btn_blue_background, null)));
 | 
			
		||||
                                    R.color.btn_green_background, null)));
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            main.mainBinding.btnRestrict.setText(R.string.restrict);
 | 
			
		||||
                            main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                    R.color.btn_orange_background, null)));
 | 
			
		||||
                        }
 | 
			
		||||
                        main.mainBinding.btnBlock.setVisibility(View.VISIBLE);
 | 
			
		||||
                        main.mainBinding.btnBlock.setOnClickListener(profileActionListener);
 | 
			
		||||
                        if (profileModel.getBlocked() == true) {
 | 
			
		||||
                            main.mainBinding.btnBlock.setText(R.string.unblock);
 | 
			
		||||
                            main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                    R.color.btn_green_background, null)));
 | 
			
		||||
                        }
 | 
			
		||||
                        else {
 | 
			
		||||
                            main.mainBinding.btnBlock.setText(R.string.block);
 | 
			
		||||
                            main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                    R.color.btn_red_background, null)));
 | 
			
		||||
                        if (profileModel.isReallyPrivate()) {
 | 
			
		||||
                            main.mainBinding.btnBlock.setVisibility(View.VISIBLE);
 | 
			
		||||
                            main.mainBinding.btnSaved.setVisibility(View.GONE);
 | 
			
		||||
                            main.mainBinding.btnTagged.setVisibility(View.GONE);
 | 
			
		||||
                            if (profileModel.getBlocked() == true) {
 | 
			
		||||
                                main.mainBinding.btnBlock.setText(R.string.unblock);
 | 
			
		||||
                                main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                        R.color.btn_green_background, null)));
 | 
			
		||||
                            } else {
 | 
			
		||||
                                main.mainBinding.btnBlock.setText(R.string.block);
 | 
			
		||||
                                main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                        R.color.btn_red_background, null)));
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            main.mainBinding.btnBlock.setVisibility(View.GONE);
 | 
			
		||||
                            main.mainBinding.btnSaved.setVisibility(View.VISIBLE);
 | 
			
		||||
                            main.mainBinding.btnTagged.setVisibility(View.VISIBLE);
 | 
			
		||||
                            if (profileModel.getBlocked() == true) {
 | 
			
		||||
                                main.mainBinding.btnSaved.setText(R.string.unblock);
 | 
			
		||||
                                main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                        R.color.btn_green_background, null)));
 | 
			
		||||
                            } else {
 | 
			
		||||
                                main.mainBinding.btnSaved.setText(R.string.block);
 | 
			
		||||
                                main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                        R.color.btn_red_background, null)));
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        main.mainBinding.btnTagged.setVisibility(View.VISIBLE);
 | 
			
		||||
                        main.mainBinding.btnSaved.setVisibility(View.VISIBLE);
 | 
			
		||||
                        main.mainBinding.btnSaved.setText(R.string.saved);
 | 
			
		||||
                        main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                R.color.btn_orange_background, null)));
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (Utils.dataBox.getFavorite(main.userQuery) != null) {
 | 
			
		||||
                        main.mainBinding.btnFollow.setText(R.string.unfavorite);
 | 
			
		||||
@ -832,7 +863,6 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
                        main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(
 | 
			
		||||
                                R.color.btn_pink_background, null)));
 | 
			
		||||
                    }
 | 
			
		||||
                    main.mainBinding.btnFollow.setOnClickListener(profileActionListener);
 | 
			
		||||
                    main.mainBinding.btnFollow.setVisibility(View.VISIBLE);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -864,7 +894,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
                span.setSpan(new StyleSpan(Typeface.BOLD), 0, followingCountStrLen, 0);
 | 
			
		||||
                main.mainBinding.mainFollowing.setText(span);
 | 
			
		||||
 | 
			
		||||
                main.mainBinding.mainFullName.setText(profileModel.getName());
 | 
			
		||||
                main.mainBinding.mainFullName.setText(Utils.isEmpty(profileModel.getName()) ? profileModel.getUsername() : profileModel.getName());
 | 
			
		||||
 | 
			
		||||
                CharSequence biography = profileModel.getBiography();
 | 
			
		||||
                main.mainBinding.mainBiography.setCaptionIsExpandable(true);
 | 
			
		||||
@ -1163,6 +1193,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
    private final View.OnClickListener profileActionListener = new View.OnClickListener() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onClick(final View v) {
 | 
			
		||||
            final boolean iamme = Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)).equals(main.profileModel.getId());
 | 
			
		||||
            if (!isLoggedIn && Utils.dataBox.getFavorite(main.userQuery) != null) {
 | 
			
		||||
                Utils.dataBox.delFavorite(new DataBox.FavoriteModel(main.userQuery,
 | 
			
		||||
                        Long.parseLong(Utils.dataBox.getFavorite(main.userQuery).split("/")[1]),
 | 
			
		||||
@ -1176,10 +1207,20 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
                new ProfileAction().execute("follow");
 | 
			
		||||
            } else if (v == main.mainBinding.btnRestrict) {
 | 
			
		||||
                new ProfileAction().execute("restrict");
 | 
			
		||||
            } else if (v == main.mainBinding.btnBlock) {
 | 
			
		||||
            } else if (v == main.mainBinding.btnSaved && !iamme) {
 | 
			
		||||
                new ProfileAction().execute("block");
 | 
			
		||||
            } else if (v == main.mainBinding.btnFollowTag) {
 | 
			
		||||
                new ProfileAction().execute("followtag");
 | 
			
		||||
            } else if (v == main.mainBinding.btnTagged) {
 | 
			
		||||
                main.startActivity(new Intent(main, SavedViewer.class)
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_INDEX, "%"+main.profileModel.getId())
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_USER, "@"+main.profileModel.getUsername())
 | 
			
		||||
                );
 | 
			
		||||
            } else if (v == main.mainBinding.btnSaved) {
 | 
			
		||||
                main.startActivity(new Intent(main, SavedViewer.class)
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_INDEX, "$"+main.profileModel.getId())
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_USER, "@"+main.profileModel.getUsername())
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -87,8 +87,6 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
 | 
			
		||||
        new CommentsFetcher(shortCode, new FetchListener<CommentModel[]>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onResult(final CommentModel[] commentModels) {
 | 
			
		||||
                commentsBinding.toolbar.progressCircular.setVisibility(View.GONE);
 | 
			
		||||
 | 
			
		||||
                commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
 | 
			
		||||
 | 
			
		||||
                commentsBinding.rvComments.setAdapter(commentsAdapter);
 | 
			
		||||
@ -105,8 +103,6 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
 | 
			
		||||
            public void onResult(final CommentModel[] commentModels) {
 | 
			
		||||
                commentsBinding.swipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
 | 
			
		||||
                commentsBinding.toolbar.progressCircular.setVisibility(View.GONE);
 | 
			
		||||
 | 
			
		||||
                commentsAdapter = new CommentsAdapter(commentModels, true, clickListener, mentionClickListener);
 | 
			
		||||
 | 
			
		||||
                commentsBinding.rvComments.setAdapter(commentsAdapter);
 | 
			
		||||
 | 
			
		||||
@ -458,6 +458,9 @@ public final class Main extends BaseLanguageActivity {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            finish();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -268,7 +268,11 @@ public final class PostViewer extends BaseLanguageActivity {
 | 
			
		||||
            final List<? extends BasePostModel> itemGetterItems;
 | 
			
		||||
            final boolean isMainSwipe;
 | 
			
		||||
 | 
			
		||||
            if (itemGetType != null && Main.itemGetter != null) {
 | 
			
		||||
            if (itemGetType == ItemGetType.SAVED_ITEMS && SavedViewer.itemGetter != null) {
 | 
			
		||||
                itemGetterItems = SavedViewer.itemGetter.get(itemGetType);
 | 
			
		||||
                isMainSwipe = !(itemGetterItems.size() < 1 || itemGetType == ItemGetType.SAVED_ITEMS && isFromShare);
 | 
			
		||||
            }
 | 
			
		||||
            else if (itemGetType != null && Main.itemGetter != null) {
 | 
			
		||||
                itemGetterItems = Main.itemGetter.get(itemGetType);
 | 
			
		||||
                isMainSwipe = !(itemGetterItems.size() < 1 || itemGetType == ItemGetType.MAIN_ITEMS && isFromShare);
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										234
									
								
								app/src/main/java/awais/instagrabber/activities/SavedViewer.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						@ -0,0 +1,234 @@
 | 
			
		||||
package awais.instagrabber.activities;
 | 
			
		||||
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.content.res.Resources;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.BuildConfig;
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.adapters.PostsAdapter;
 | 
			
		||||
import awais.instagrabber.asyncs.PostsFetcher;
 | 
			
		||||
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
 | 
			
		||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
 | 
			
		||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
 | 
			
		||||
import awais.instagrabber.databinding.ActivitySavedBinding;
 | 
			
		||||
import awais.instagrabber.interfaces.FetchListener;
 | 
			
		||||
import awais.instagrabber.interfaces.ItemGetter;
 | 
			
		||||
import awais.instagrabber.models.BasePostModel;
 | 
			
		||||
import awais.instagrabber.models.PostModel;
 | 
			
		||||
import awais.instagrabber.models.enums.DownloadMethod;
 | 
			
		||||
import awais.instagrabber.models.enums.ItemGetType;
 | 
			
		||||
import awais.instagrabber.utils.Constants;
 | 
			
		||||
import awais.instagrabber.utils.Utils;
 | 
			
		||||
import awaisomereport.LogCollector;
 | 
			
		||||
 | 
			
		||||
import static awais.instagrabber.utils.Constants.AUTOLOAD_POSTS;
 | 
			
		||||
import static awais.instagrabber.utils.Utils.logCollector;
 | 
			
		||||
 | 
			
		||||
public final class SavedViewer extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener {
 | 
			
		||||
    private static AsyncTask<?, ?, ?> currentlyExecuting;
 | 
			
		||||
    public static ItemGetter itemGetter;
 | 
			
		||||
    private PostsAdapter postsAdapter;
 | 
			
		||||
    private boolean hasNextPage, autoloadPosts;
 | 
			
		||||
    //private CommentModel commentModel;
 | 
			
		||||
    private ActivitySavedBinding savedBinding;
 | 
			
		||||
    private String action, username, endCursor;
 | 
			
		||||
    private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
 | 
			
		||||
    private RecyclerLazyLoader lazyLoader;
 | 
			
		||||
    private Resources resources;
 | 
			
		||||
    private ArrayList<PostModel> selectedItems = new ArrayList<>();
 | 
			
		||||
    private final ArrayList<PostModel> allItems = new ArrayList<>();
 | 
			
		||||
    private MenuItem downloadAction;
 | 
			
		||||
 | 
			
		||||
    private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onResult(final PostModel[] result) {
 | 
			
		||||
            if (result != null) {
 | 
			
		||||
                final int oldSize = allItems.size();
 | 
			
		||||
                allItems.addAll(Arrays.asList(result));
 | 
			
		||||
 | 
			
		||||
                postsAdapter.notifyItemRangeInserted(oldSize, result.length);
 | 
			
		||||
 | 
			
		||||
                savedBinding.mainPosts.post(() -> {
 | 
			
		||||
                    savedBinding.mainPosts.setNestedScrollingEnabled(true);
 | 
			
		||||
                    savedBinding.mainPosts.setVisibility(View.VISIBLE);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                final PostModel model = result[result.length - 1];
 | 
			
		||||
                if (model != null) {
 | 
			
		||||
                    endCursor = model.getEndCursor();
 | 
			
		||||
 | 
			
		||||
                    hasNextPage = model.hasNextPage();
 | 
			
		||||
                    if (autoloadPosts && hasNextPage)
 | 
			
		||||
                        currentlyExecuting = new PostsFetcher(action, endCursor, this)
 | 
			
		||||
                                .setUsername(username).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
                    else {
 | 
			
		||||
                        savedBinding.swipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
                    }
 | 
			
		||||
                    model.setPageCursor(false, null);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                savedBinding.swipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
                Toast.makeText(getApplicationContext(), R.string.empty_list, Toast.LENGTH_SHORT).show();
 | 
			
		||||
                finish();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(@Nullable final Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        savedBinding = ActivitySavedBinding.inflate(getLayoutInflater());
 | 
			
		||||
        setContentView(savedBinding.getRoot());
 | 
			
		||||
        savedBinding.swipeRefreshLayout.setOnRefreshListener(this);
 | 
			
		||||
        autoloadPosts = Utils.settingsHelper.getBoolean(AUTOLOAD_POSTS);
 | 
			
		||||
        savedBinding.mainPosts.setNestedScrollingEnabled(false);
 | 
			
		||||
        final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(this, Utils.convertDpToPx(110));
 | 
			
		||||
        savedBinding.mainPosts.setLayoutManager(layoutManager);
 | 
			
		||||
        savedBinding.mainPosts.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(4)));
 | 
			
		||||
 | 
			
		||||
        final Intent intent = getIntent();
 | 
			
		||||
        if (intent == null || !intent.hasExtra(Constants.EXTRAS_INDEX)
 | 
			
		||||
                || Utils.isEmpty((action = intent.getStringExtra(Constants.EXTRAS_INDEX)))
 | 
			
		||||
                || !intent.hasExtra(Constants.EXTRAS_USER)
 | 
			
		||||
                || Utils.isEmpty((username = intent.getStringExtra(Constants.EXTRAS_USER)))) {
 | 
			
		||||
            Utils.errorFinish(this);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        savedBinding.mainPosts.setAdapter(postsAdapter = new PostsAdapter(allItems, v -> {
 | 
			
		||||
            final Object tag = v.getTag();
 | 
			
		||||
            if (tag instanceof PostModel) {
 | 
			
		||||
                final PostModel postModel = (PostModel) tag;
 | 
			
		||||
 | 
			
		||||
                if (postsAdapter.isSelecting) toggleSelection(postModel);
 | 
			
		||||
                else startActivity(new Intent(this, PostViewer.class)
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_INDEX, postModel.getPosition())
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_POST, postModel)
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_USER, username)
 | 
			
		||||
                        .putExtra(Constants.EXTRAS_TYPE, ItemGetType.SAVED_ITEMS));
 | 
			
		||||
            }
 | 
			
		||||
        }, v -> {
 | 
			
		||||
            final Object tag = v.getTag();
 | 
			
		||||
            if (tag instanceof PostModel) {
 | 
			
		||||
                postsAdapter.isSelecting = true;
 | 
			
		||||
                toggleSelection((PostModel) tag);
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        }));
 | 
			
		||||
        savedBinding.swipeRefreshLayout.setRefreshing(true);
 | 
			
		||||
        setSupportActionBar(savedBinding.toolbar.toolbar);
 | 
			
		||||
        savedBinding.toolbar.toolbar.setTitle((action.charAt(0) == '$' ? R.string.saved :
 | 
			
		||||
                (action.charAt(0) == '%' ? R.string.tagged : R.string.liked)));
 | 
			
		||||
        savedBinding.toolbar.toolbar.setSubtitle(username);
 | 
			
		||||
 | 
			
		||||
        lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
 | 
			
		||||
            if (!autoloadPosts && hasNextPage) {
 | 
			
		||||
                savedBinding.swipeRefreshLayout.setRefreshing(true);
 | 
			
		||||
                stopCurrentExecutor();
 | 
			
		||||
                currentlyExecuting = new PostsFetcher(action, endCursor, postsFetchListener)
 | 
			
		||||
                        .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
                endCursor = null;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        savedBinding.mainPosts.addOnScrollListener(lazyLoader);
 | 
			
		||||
 | 
			
		||||
        itemGetter = itemGetType -> {
 | 
			
		||||
            if (itemGetType == ItemGetType.SAVED_ITEMS) return allItems;
 | 
			
		||||
            return null;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        new PostsFetcher(action, postsFetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean onCreateOptionsMenu(final Menu menu) {
 | 
			
		||||
        getMenuInflater().inflate(R.menu.saved, menu);
 | 
			
		||||
 | 
			
		||||
        downloadAction = menu.findItem(R.id.downloadAction);
 | 
			
		||||
        downloadAction.setVisible(false);
 | 
			
		||||
 | 
			
		||||
        downloadAction.setOnMenuItemClickListener(item -> {
 | 
			
		||||
            if (selectedItems.size() > 0) {
 | 
			
		||||
                Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        });
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deselectSelection(final BasePostModel postModel) {
 | 
			
		||||
        if (postModel instanceof PostModel) {
 | 
			
		||||
            selectedItems.remove(postModel);
 | 
			
		||||
            postModel.setSelected(false);
 | 
			
		||||
            if (postsAdapter != null) notifyAdapter((PostModel) postModel);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onRefresh() {
 | 
			
		||||
        if (lazyLoader != null) lazyLoader.resetState();
 | 
			
		||||
        stopCurrentExecutor();
 | 
			
		||||
        allItems.clear();
 | 
			
		||||
        selectedItems.clear();
 | 
			
		||||
        if (postsAdapter != null) {
 | 
			
		||||
            postsAdapter.isSelecting = false;
 | 
			
		||||
            postsAdapter.notifyDataSetChanged();
 | 
			
		||||
        }
 | 
			
		||||
        savedBinding.swipeRefreshLayout.setRefreshing(true);
 | 
			
		||||
        new PostsFetcher(action, postsFetchListener).setUsername(username).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
 | 
			
		||||
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 | 
			
		||||
        if (requestCode == 8020 && grantResults[0] == PackageManager.PERMISSION_GRANTED && selectedItems.size() > 0)
 | 
			
		||||
            Utils.batchDownload(this, null, DownloadMethod.DOWNLOAD_SAVED, selectedItems);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void stopCurrentExecutor() {
 | 
			
		||||
        if (currentlyExecuting != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                currentlyExecuting.cancel(true);
 | 
			
		||||
            } catch (final Exception e) {
 | 
			
		||||
                if (logCollector != null)
 | 
			
		||||
                    logCollector.appendException(e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor");
 | 
			
		||||
                if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void toggleSelection(final PostModel postModel) {
 | 
			
		||||
        if (postModel != null && postsAdapter != null) {
 | 
			
		||||
            if (postModel.isSelected()) selectedItems.remove(postModel);
 | 
			
		||||
            else selectedItems.add(postModel);
 | 
			
		||||
            postModel.setSelected(!postModel.isSelected());
 | 
			
		||||
            notifyAdapter(postModel);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void notifyAdapter(final PostModel postModel) {
 | 
			
		||||
        if (selectedItems.size() < 1) postsAdapter.isSelecting = false;
 | 
			
		||||
        if (postModel.getPosition() < 0) postsAdapter.notifyDataSetChanged();
 | 
			
		||||
        else postsAdapter.notifyItemChanged(postModel.getPosition(), postModel);
 | 
			
		||||
 | 
			
		||||
        if (downloadAction != null) {
 | 
			
		||||
            downloadAction.setVisible(postsAdapter.isSelecting);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -194,6 +194,15 @@ public final class StoryViewer extends BaseLanguageActivity {
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        storyViewerBinding.spotify.setOnClickListener(v -> {
 | 
			
		||||
            final Object tag = v.getTag();
 | 
			
		||||
            if (tag instanceof CharSequence) {
 | 
			
		||||
                final Intent intent = new Intent(Intent.ACTION_VIEW);
 | 
			
		||||
                intent.setData(Uri.parse(tag.toString()));
 | 
			
		||||
                startActivity(intent);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        storyViewerBinding.viewStoryPost.setOnClickListener(v -> {
 | 
			
		||||
            final Object tag = v.getTag();
 | 
			
		||||
            if (tag instanceof CharSequence) startActivity(new Intent(this, PostViewer.class)
 | 
			
		||||
@ -397,6 +406,10 @@ public final class StoryViewer extends BaseLanguageActivity {
 | 
			
		||||
        storyViewerBinding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE);
 | 
			
		||||
        storyViewerBinding.viewStoryPost.setTag(shortCode);
 | 
			
		||||
 | 
			
		||||
        final String spotify = currentStory.getSpotify();
 | 
			
		||||
        storyViewerBinding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE);
 | 
			
		||||
        storyViewerBinding.spotify.setTag(spotify);
 | 
			
		||||
 | 
			
		||||
        final PollModel poll = currentStory.getPoll();
 | 
			
		||||
        storyViewerBinding.interactStory.setVisibility(poll != null ? View.VISIBLE : View.GONE);
 | 
			
		||||
        storyViewerBinding.interactStory.setText(R.string.vote_story_poll);
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,9 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
 | 
			
		||||
    @Override
 | 
			
		||||
    protected PostModel[] doInBackground(final Void... voids) {
 | 
			
		||||
        final boolean isHashTag = id.charAt(0) == '#';
 | 
			
		||||
        final boolean isSaved = id.charAt(0) == '$';
 | 
			
		||||
        final boolean isTagged = id.charAt(0) == '%';
 | 
			
		||||
        //final boolean isLiked = id.charAt(0) == '^';
 | 
			
		||||
        final boolean isLocation = id.contains("/");
 | 
			
		||||
 | 
			
		||||
        final String url;
 | 
			
		||||
@ -58,6 +61,12 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
 | 
			
		||||
        else if (isLocation)
 | 
			
		||||
            url = "https://www.instagram.com/graphql/query/?query_hash=36bd0f2bf5911908de389b8ceaa3be6d&variables=" +
 | 
			
		||||
                    "{\"id\":\""+ id.split("/")[0] +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
 | 
			
		||||
        else if (isSaved)
 | 
			
		||||
            url = "https://www.instagram.com/graphql/query/?query_hash=8c86fed24fa03a8a2eea2a70a80c7b6b&variables=" +
 | 
			
		||||
                    "{\"id\":\""+ id.substring(1) +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
 | 
			
		||||
        else if (isTagged)
 | 
			
		||||
            url = "https://www.instagram.com/graphql/query/?query_hash=ff260833edf142911047af6024eb634a&variables=" +
 | 
			
		||||
                    "{\"id\":\""+ id.substring(1) +"\",\"first\":150,\"after\":\"" + endCursor + "\"}";
 | 
			
		||||
        else
 | 
			
		||||
            url = "https://www.instagram.com/graphql/query/?query_id=17880160963012870&id=" + id + "&first=50&after=" + endCursor;
 | 
			
		||||
 | 
			
		||||
@ -80,7 +89,9 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
 | 
			
		||||
                        .getJSONObject(isHashTag ? Constants.EXTRAS_HASHTAG :
 | 
			
		||||
                                (isLocation ? Constants.EXTRAS_LOCATION : Constants.EXTRAS_USER))
 | 
			
		||||
                        .getJSONObject(isHashTag ? "edge_hashtag_to_media" :
 | 
			
		||||
                                (isLocation ? "edge_location_to_media" : "edge_owner_to_timeline_media"));
 | 
			
		||||
                                (isLocation ? "edge_location_to_media" :
 | 
			
		||||
                                        (isSaved ? "edge_saved_media" :
 | 
			
		||||
                                                (isTagged ? "edge_user_to_photos_of_you" : "edge_owner_to_timeline_media"))));
 | 
			
		||||
 | 
			
		||||
                final String endCursor;
 | 
			
		||||
                final boolean hasNextPage;
 | 
			
		||||
@ -116,6 +127,7 @@ public final class PostsFetcher extends AsyncTask<Void, Void, PostModel[]> {
 | 
			
		||||
                            mediaNode.optBoolean("viewer_has_saved"), mediaNode.getJSONObject("edge_liked_by").getLong("count"));
 | 
			
		||||
 | 
			
		||||
                    Utils.checkExistence(downloadDir, customDir, username, isSlider, models[i]);
 | 
			
		||||
                    Utils.checkExistence(downloadDir, customDir, "@"+username, isSlider, models[i]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (models[models.length - 1] != null)
 | 
			
		||||
 | 
			
		||||
@ -74,6 +74,9 @@ public final class StoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[]
 | 
			
		||||
                        if (isVideo && videoResources != null)
 | 
			
		||||
                            models[i].setVideoUrl(Utils.getHighQualityPost(videoResources, true));
 | 
			
		||||
 | 
			
		||||
                        if (!data.isNull("story_app_attribution"))
 | 
			
		||||
                            models[i].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]);
 | 
			
		||||
 | 
			
		||||
                        for (int j = 0; j < tappableLength; ++j) {
 | 
			
		||||
                            JSONObject tappableObject = tappableObjects.getJSONObject(j);
 | 
			
		||||
                            if (tappableObject.optString("__typename").equals("GraphTappableFeedMedia")) {
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ public final class StoryModel implements Serializable {
 | 
			
		||||
    private final String storyMediaId, storyUrl, username;
 | 
			
		||||
    private final MediaItemType itemType;
 | 
			
		||||
    private final long timestamp;
 | 
			
		||||
    private String videoUrl, tappableShortCode;
 | 
			
		||||
    private String videoUrl, tappableShortCode, spotify;
 | 
			
		||||
    private PollModel poll;
 | 
			
		||||
    private int position;
 | 
			
		||||
    private boolean isCurrentSlide = false;
 | 
			
		||||
@ -57,6 +57,14 @@ public final class StoryModel implements Serializable {
 | 
			
		||||
        this.videoUrl = videoUrl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getSpotify() {
 | 
			
		||||
        return spotify;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setSpotify(final String spotify) {
 | 
			
		||||
        this.spotify = spotify;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setTappableShortCode(final String tappableShortCode) {
 | 
			
		||||
        this.tappableShortCode = tappableShortCode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -5,5 +5,6 @@ public enum DownloadMethod {
 | 
			
		||||
    DOWNLOAD_DISCOVER,
 | 
			
		||||
    DOWNLOAD_FEED,
 | 
			
		||||
    DOWNLOAD_POST_VIEWER,
 | 
			
		||||
    DOWNLOAD_DIRECT;
 | 
			
		||||
    DOWNLOAD_DIRECT,
 | 
			
		||||
    DOWNLOAD_SAVED;
 | 
			
		||||
}
 | 
			
		||||
@ -6,4 +6,5 @@ public enum ItemGetType implements Serializable {
 | 
			
		||||
    MAIN_ITEMS,
 | 
			
		||||
    DISCOVER_ITEMS,
 | 
			
		||||
    FEED_ITEMS,
 | 
			
		||||
    SAVED_ITEMS
 | 
			
		||||
}
 | 
			
		||||
@ -62,6 +62,7 @@ import java.util.regex.Pattern;
 | 
			
		||||
import awais.instagrabber.BuildConfig;
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.activities.Main;
 | 
			
		||||
import awais.instagrabber.activities.SavedViewer;
 | 
			
		||||
import awais.instagrabber.asyncs.DownloadAsync;
 | 
			
		||||
import awais.instagrabber.asyncs.PostFetcher;
 | 
			
		||||
import awais.instagrabber.customviews.CommentMentionClickSpan;
 | 
			
		||||
@ -852,6 +853,7 @@ public final class Utils {
 | 
			
		||||
 | 
			
		||||
        if (dir.exists() || dir.mkdirs()) {
 | 
			
		||||
            final Main main = method != DownloadMethod.DOWNLOAD_FEED && context instanceof Main ? (Main) context : null;
 | 
			
		||||
            final SavedViewer saved = method == DownloadMethod.DOWNLOAD_SAVED && context instanceof SavedViewer ? (SavedViewer) context : null;
 | 
			
		||||
 | 
			
		||||
            final int itemsToDownloadSize = itemsToDownload.size();
 | 
			
		||||
 | 
			
		||||
@ -859,12 +861,34 @@ public final class Utils {
 | 
			
		||||
            for (int i = itemsToDownloadSize - 1; i >= 0; i--) {
 | 
			
		||||
                final BasePostModel selectedItem = itemsToDownload.get(i);
 | 
			
		||||
 | 
			
		||||
                if (main == null) {
 | 
			
		||||
                if (main == null && saved == null) {
 | 
			
		||||
                    new DownloadAsync(context,
 | 
			
		||||
                            selectedItem.getDisplayUrl(),
 | 
			
		||||
                            getDownloadSaveFile(finalDir, selectedItem, ""),
 | 
			
		||||
                            null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
 | 
			
		||||
                } else if (saved != null) {
 | 
			
		||||
                    new PostFetcher(selectedItem.getShortCode(), result -> {
 | 
			
		||||
                        if (result != null) {
 | 
			
		||||
                            final int resultsSize = result.length;
 | 
			
		||||
                            final boolean multiResult = resultsSize > 1;
 | 
			
		||||
 | 
			
		||||
                            for (int j = 0; j < resultsSize; j++) {
 | 
			
		||||
                                final BasePostModel model = result[j];
 | 
			
		||||
                                final File saveFile = getDownloadSaveFile(finalDir, model, multiResult ? "_slide_" + (j + 1) : "");
 | 
			
		||||
 | 
			
		||||
                                new DownloadAsync(context,
 | 
			
		||||
                                        model.getDisplayUrl(),
 | 
			
		||||
                                        saveFile,
 | 
			
		||||
                                        file -> {
 | 
			
		||||
                                            model.setDownloaded(true);
 | 
			
		||||
                                            saved.deselectSelection(selectedItem);
 | 
			
		||||
                                        }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            saved.deselectSelection(selectedItem);
 | 
			
		||||
                        }
 | 
			
		||||
                    }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
                } else {
 | 
			
		||||
                    new PostFetcher(selectedItem.getShortCode(), result -> {
 | 
			
		||||
                        if (result != null) {
 | 
			
		||||
@ -986,16 +1010,7 @@ public final class Utils {
 | 
			
		||||
            final String fileWithoutPrefix = fileName + '0' + extension;
 | 
			
		||||
            exists = new File(downloadDir, fileWithoutPrefix).exists();
 | 
			
		||||
            if (!exists) {
 | 
			
		||||
                if (customDir != null) exists = new File(customDir, fileWithoutPrefix).exists();
 | 
			
		||||
                if (!exists && !Utils.isEmpty(username)) {
 | 
			
		||||
                    exists = new File(new File(downloadDir, username), fileWithoutPrefix).exists();
 | 
			
		||||
                }
 | 
			
		||||
                if (!exists && customDir != null)
 | 
			
		||||
                    exists = new File(new File(customDir, username), fileWithoutPrefix).exists();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!exists && isSlider) {
 | 
			
		||||
                final String fileWithPrefix = fileName + "[\\d]+_slide_[\\d]+" + extension;
 | 
			
		||||
                final String fileWithPrefix = fileName + "[\\d]+(|_slide_[\\d]+)(\\.mp4|\\" + extension + ")";
 | 
			
		||||
                final FilenameFilter filenameFilter = (dir, name) -> Pattern.matches(fileWithPrefix, name);
 | 
			
		||||
 | 
			
		||||
                File[] files = downloadDir.listFiles(filenameFilter);
 | 
			
		||||
@ -1163,6 +1178,9 @@ public final class Utils {
 | 
			
		||||
                    if (isVideo && data.has("video_resources"))
 | 
			
		||||
                        storyModels[j].setVideoUrl(Utils.getHighQualityPost(data.getJSONArray("video_resources"), true));
 | 
			
		||||
 | 
			
		||||
                    if (!data.isNull("story_app_attribution"))
 | 
			
		||||
                        storyModels[j].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]);
 | 
			
		||||
 | 
			
		||||
                    if (hasTappableObjecs) {
 | 
			
		||||
                        for (int k = 0; k < tappableLength; ++k) {
 | 
			
		||||
                            JSONObject jsonObject = tappableObjects.getJSONObject(k);
 | 
			
		||||
 | 
			
		||||
@ -110,6 +110,7 @@ public final class LogCollector {
 | 
			
		||||
        ASYNC_LOCATION_FETCHER("async-location-fetcher.txt"),
 | 
			
		||||
        ASYNC_PROFILE_FETCHER("async-profile-fetcher.txt"),
 | 
			
		||||
        ASYNC_PROFILE_PICTURE_FETCHER("async-pfp-fetcher.txt"),
 | 
			
		||||
        ASYNC_SAVED_FETCHER("async-saved-fetcher.txt"),
 | 
			
		||||
        ASYNC_STORY_STATUS_FETCHER("async-story-status-fetcher.txt"),
 | 
			
		||||
        ASYNC_DISCOVER_FETCHER("async-discover-fetcher.txt"),
 | 
			
		||||
        ASYNC_COMMENTS_FETCHER("async-comments-fetcher.txt"),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable-anydpi/ic_bookmark.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,10 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:tint="#333333">
 | 
			
		||||
  <path
 | 
			
		||||
      android:fillColor="@android:color/white"
 | 
			
		||||
      android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable-anydpi/ic_like.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,10 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:width="24dp"
 | 
			
		||||
    android:height="24dp"
 | 
			
		||||
    android:viewportWidth="24"
 | 
			
		||||
    android:viewportHeight="24"
 | 
			
		||||
    android:tint="#333333">
 | 
			
		||||
  <path
 | 
			
		||||
      android:fillColor="@android:color/white"
 | 
			
		||||
      android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_bookmark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 210 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-hdpi/ic_like.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 333 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-mdpi/ic_bookmark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 160 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-mdpi/ic_like.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 239 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xhdpi/ic_bookmark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 264 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xhdpi/ic_like.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 415 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_bookmark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 380 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_like.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 570 B  | 
@ -112,26 +112,25 @@
 | 
			
		||||
                                android:id="@+id/profileActions"
 | 
			
		||||
                                android:layout_width="match_parent"
 | 
			
		||||
                                android:layout_height="wrap_content"
 | 
			
		||||
                                android:layout_below="@id/mainUrl"
 | 
			
		||||
                                android:weightSum="3">
 | 
			
		||||
                                android:layout_below="@id/mainUrl">
 | 
			
		||||
                                <androidx.appcompat.widget.AppCompatButton
 | 
			
		||||
                                    android:id="@+id/btnFollow"
 | 
			
		||||
                                    android:layout_width="match_parent"
 | 
			
		||||
                                    android:layout_height="wrap_content"
 | 
			
		||||
                                    android:layout_height="match_parent"
 | 
			
		||||
                                    android:layout_marginLeft="8dp"
 | 
			
		||||
                                    android:layout_marginStart="2dp"
 | 
			
		||||
                                    android:layout_marginRight="8dp"
 | 
			
		||||
                                    android:layout_weight="1"
 | 
			
		||||
                                    android:text="@string/follow"
 | 
			
		||||
                                    android:textColor="@color/btn_pink_text_color"
 | 
			
		||||
                                    android:textSize="18sp"
 | 
			
		||||
                                    android:textSize="16sp"
 | 
			
		||||
                                    app:backgroundTint="@color/btn_pink_background"
 | 
			
		||||
                                    android:visibility="gone" />
 | 
			
		||||
 | 
			
		||||
                                <androidx.appcompat.widget.AppCompatButton
 | 
			
		||||
                                    android:id="@+id/btnRestrict"
 | 
			
		||||
                                    android:layout_width="match_parent"
 | 
			
		||||
                                    android:layout_height="wrap_content"
 | 
			
		||||
                                    android:layout_height="match_parent"
 | 
			
		||||
                                    android:layout_marginLeft="8dp"
 | 
			
		||||
                                    android:layout_marginEnd="1dp"
 | 
			
		||||
                                    android:layout_marginStart="1dp"
 | 
			
		||||
@ -139,31 +138,69 @@
 | 
			
		||||
                                    android:layout_weight="1"
 | 
			
		||||
                                    android:text="@string/restrict"
 | 
			
		||||
                                    android:textColor="@color/btn_orange_text_color"
 | 
			
		||||
                                    android:textSize="18sp"
 | 
			
		||||
                                    android:textSize="16sp"
 | 
			
		||||
                                    app:backgroundTint="@color/btn_orange_background"
 | 
			
		||||
                                    android:visibility="gone" />
 | 
			
		||||
 | 
			
		||||
                                <!-- only for invisible private accounts -->
 | 
			
		||||
                                <androidx.appcompat.widget.AppCompatButton
 | 
			
		||||
                                    android:id="@+id/btnBlock"
 | 
			
		||||
                                    android:layout_width="match_parent"
 | 
			
		||||
                                    android:layout_height="wrap_content"
 | 
			
		||||
                                    android:layout_height="match_parent"
 | 
			
		||||
                                    android:layout_marginLeft="8dp"
 | 
			
		||||
                                    android:layout_marginEnd="2dp"
 | 
			
		||||
                                    android:layout_marginRight="8dp"
 | 
			
		||||
                                    android:layout_weight="1"
 | 
			
		||||
                                    android:text="@string/block"
 | 
			
		||||
                                    android:textColor="@color/btn_red_text_color"
 | 
			
		||||
                                    android:textSize="18sp"
 | 
			
		||||
                                    android:textSize="16sp"
 | 
			
		||||
                                    app:backgroundTint="@color/btn_red_background"
 | 
			
		||||
                                    android:visibility="gone" />
 | 
			
		||||
                            </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_marginLeft="8dp"
 | 
			
		||||
                                    android:layout_marginStart="2dp"
 | 
			
		||||
                                    android:layout_marginRight="8dp"
 | 
			
		||||
                                    android:layout_weight="1"
 | 
			
		||||
                                    android:text="@string/tagged"
 | 
			
		||||
                                    android:textColor="@color/btn_blue_text_color"
 | 
			
		||||
                                    android:textSize="16sp"
 | 
			
		||||
                                    app:backgroundTint="@color/btn_blue_background"
 | 
			
		||||
                                    android:visibility="gone" />
 | 
			
		||||
 | 
			
		||||
                                <!-- also used as block -->
 | 
			
		||||
                                <androidx.appcompat.widget.AppCompatButton
 | 
			
		||||
                                    android:id="@+id/btnSaved"
 | 
			
		||||
                                    android:layout_width="match_parent"
 | 
			
		||||
                                    android:layout_height="wrap_content"
 | 
			
		||||
                                    android:layout_marginLeft="8dp"
 | 
			
		||||
                                    android:layout_marginEnd="1dp"
 | 
			
		||||
                                    android:layout_marginStart="1dp"
 | 
			
		||||
                                    android:layout_marginRight="8dp"
 | 
			
		||||
                                    android:layout_weight="1"
 | 
			
		||||
                                    android:text="@string/saved"
 | 
			
		||||
                                    android:textColor="@color/btn_orange_text_color"
 | 
			
		||||
                                    android:textSize="16sp"
 | 
			
		||||
                                    app:backgroundTint="@color/btn_orange_background"
 | 
			
		||||
                                    android:visibility="gone" />
 | 
			
		||||
                            </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/profileActions"
 | 
			
		||||
                                android:layout_below="@id/myActions"
 | 
			
		||||
                                android:clipToPadding="false"
 | 
			
		||||
                                android:orientation="horizontal"
 | 
			
		||||
                                android:paddingStart="5dp"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								app/src/main/res/layout/activity_saved.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,31 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent"
 | 
			
		||||
    android:orientation="vertical"
 | 
			
		||||
    tools:context=".activities.SavedViewer">
 | 
			
		||||
 | 
			
		||||
    <include
 | 
			
		||||
        android:id="@+id/toolbar"
 | 
			
		||||
        layout="@layout/layout_include_toolbar" />
 | 
			
		||||
 | 
			
		||||
    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
 | 
			
		||||
        android:id="@+id/swipeRefreshLayout"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="match_parent"
 | 
			
		||||
        android:tag="@android:string/yes">
 | 
			
		||||
 | 
			
		||||
        <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
            android:id="@+id/mainPosts"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="match_parent"
 | 
			
		||||
            android:clipToPadding="false"
 | 
			
		||||
            android:paddingStart="8dp"
 | 
			
		||||
            android:paddingLeft="8dp"
 | 
			
		||||
            android:paddingEnd="8dp"
 | 
			
		||||
            android:paddingRight="8dp"
 | 
			
		||||
            tools:listitem="@layout/item_post" />
 | 
			
		||||
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
@ -44,7 +44,7 @@
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:layout_weight="0.3"
 | 
			
		||||
            android:background="#0000"
 | 
			
		||||
            android:weightSum="2"
 | 
			
		||||
            android:weightSum="3"
 | 
			
		||||
            android:layout_gravity="bottom">
 | 
			
		||||
            <androidx.appcompat.widget.AppCompatButton
 | 
			
		||||
                android:id="@+id/viewStoryPost"
 | 
			
		||||
@ -64,6 +64,15 @@
 | 
			
		||||
                android:textColor="@color/btn_blue_text_color"
 | 
			
		||||
                android:visibility="gone"
 | 
			
		||||
                app:backgroundTint="@color/btn_blue_background" />
 | 
			
		||||
            <androidx.appcompat.widget.AppCompatButton
 | 
			
		||||
                android:id="@+id/spotify"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:layout_weight="1"
 | 
			
		||||
                android:text="@string/spotify"
 | 
			
		||||
                android:textColor="@color/btn_green_text_color"
 | 
			
		||||
                android:visibility="gone"
 | 
			
		||||
                app:backgroundTint="@color/btn_green_background" />
 | 
			
		||||
        </androidx.appcompat.widget.LinearLayoutCompat>
 | 
			
		||||
    </FrameLayout>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,14 +6,4 @@
 | 
			
		||||
    android:layout_height="?actionBarSize"
 | 
			
		||||
    android:background="@null"
 | 
			
		||||
    app:title="@string/app_name">
 | 
			
		||||
 | 
			
		||||
    <ProgressBar
 | 
			
		||||
        android:id="@+id/progress_circular"
 | 
			
		||||
        android:layout_width="?actionBarSize"
 | 
			
		||||
        android:layout_height="?actionBarSize"
 | 
			
		||||
        android:indeterminate="true"
 | 
			
		||||
        android:indeterminateBehavior="cycle"
 | 
			
		||||
        android:indeterminateOnly="true"
 | 
			
		||||
        android:padding="8dp"
 | 
			
		||||
        android:visibility="gone" />
 | 
			
		||||
</androidx.appcompat.widget.Toolbar>
 | 
			
		||||
							
								
								
									
										9
									
								
								app/src/main/res/menu/saved.xml
									
									
									
									
									
										Executable file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
			
		||||
<?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/downloadAction"
 | 
			
		||||
        android:icon="@drawable/ic_download"
 | 
			
		||||
        app:showAsAction="always|collapseActionView" />
 | 
			
		||||
</menu>
 | 
			
		||||
@ -60,11 +60,13 @@
 | 
			
		||||
    <string name="show_stories">Show stories</string>
 | 
			
		||||
    <string name="no_more_stories">No more stories!</string>
 | 
			
		||||
    <string name="view_story_post">View Story Post</string>
 | 
			
		||||
    <string name="spotify">Spotify</string>
 | 
			
		||||
    <string name="vote_story_poll">Vote</string>
 | 
			
		||||
    <string name="votef_story_poll">Vote successful!</string>
 | 
			
		||||
    <string name="voted_story_poll">You have already voted!</string>
 | 
			
		||||
    <string name="priv_acc">This Account is Private</string>
 | 
			
		||||
    <string name="empty_acc">This Account has No Posts</string>
 | 
			
		||||
    <string name="empty_list">No Such Posts!</string>
 | 
			
		||||
    <string name="curr_version">Current version: v%s</string>
 | 
			
		||||
    <string name="read_more">read more…</string>
 | 
			
		||||
    <string name="login">Login</string>
 | 
			
		||||
@ -75,6 +77,10 @@
 | 
			
		||||
    <string name="telegram_link">Join Telegram Group</string>
 | 
			
		||||
    <string name="matrix_link">Join Matrix Room</string>
 | 
			
		||||
 | 
			
		||||
    <string name="liked">Liked</string>
 | 
			
		||||
    <string name="saved">Saved</string>
 | 
			
		||||
    <string name="tagged">Tagged</string>
 | 
			
		||||
 | 
			
		||||
    <string name="like">Like (%s)</string>
 | 
			
		||||
    <string name="unlike">Unlike (%s)</string>
 | 
			
		||||
    <string name="bookmark">Bookmark</string>
 | 
			
		||||
 | 
			
		||||