mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-11-04 13:35:36 +00:00 
			
		
		
		
	Merge branch 'master' into add-sentry
This commit is contained in:
		
						commit
						fba0a751b1
					
				@ -132,6 +132,6 @@ dependencies {
 | 
			
		||||
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
 | 
			
		||||
 | 
			
		||||
    githubImplementation 'io.sentry:sentry-android:4.3.0'
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,42 @@
 | 
			
		||||
package awais.instagrabber.adapters;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.adapters.viewholder.dialogs.KeywordsFilterDialogViewHolder;
 | 
			
		||||
 | 
			
		||||
public class KeywordsFilterAdapter extends RecyclerView.Adapter<KeywordsFilterDialogViewHolder> {
 | 
			
		||||
 | 
			
		||||
    private final Context context;
 | 
			
		||||
    private final ArrayList<String> items;
 | 
			
		||||
 | 
			
		||||
    public KeywordsFilterAdapter(Context context, ArrayList<String> items){
 | 
			
		||||
        this.context = context;
 | 
			
		||||
        this.items = items;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public KeywordsFilterDialogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
 | 
			
		||||
        final View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_keyword, parent, false);
 | 
			
		||||
        return new KeywordsFilterDialogViewHolder(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(@NonNull KeywordsFilterDialogViewHolder holder, int position) {
 | 
			
		||||
        holder.bind(items, position, context, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemCount() {
 | 
			
		||||
        return items.size();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,51 @@
 | 
			
		||||
package awais.instagrabber.adapters.viewholder.dialogs;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.adapters.KeywordsFilterAdapter;
 | 
			
		||||
import awais.instagrabber.utils.Constants;
 | 
			
		||||
import awais.instagrabber.utils.SettingsHelper;
 | 
			
		||||
 | 
			
		||||
public class KeywordsFilterDialogViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
 | 
			
		||||
    private final Button deleteButton;
 | 
			
		||||
    private final TextView item;
 | 
			
		||||
 | 
			
		||||
    public KeywordsFilterDialogViewHolder(@NonNull View itemView) {
 | 
			
		||||
        super(itemView);
 | 
			
		||||
        deleteButton = itemView.findViewById(R.id.keyword_delete);
 | 
			
		||||
        item = itemView.findViewById(R.id.keyword_text);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void bind(ArrayList<String> items, int position, Context context, KeywordsFilterAdapter adapter){
 | 
			
		||||
        item.setText(items.get(position));
 | 
			
		||||
        deleteButton.setOnClickListener(view -> {
 | 
			
		||||
            final String s = items.get(position);
 | 
			
		||||
            SettingsHelper settingsHelper = new SettingsHelper(context);
 | 
			
		||||
            items.remove(position);
 | 
			
		||||
            settingsHelper.putStringSet(Constants.KEYWORD_FILTERS, new HashSet<>(items));
 | 
			
		||||
            adapter.notifyDataSetChanged();
 | 
			
		||||
            final String message = context.getString(R.string.removed_keywords, s);
 | 
			
		||||
            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Button getDeleteButton(){
 | 
			
		||||
        return deleteButton;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TextView getTextView(){
 | 
			
		||||
        return item;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -40,7 +40,10 @@ public class FeedPostFetchService implements PostFetcher.PostFetchService {
 | 
			
		||||
                } else if (result == null) return;
 | 
			
		||||
                nextCursor = result.getNextCursor();
 | 
			
		||||
                hasNextPage = result.hasNextPage();
 | 
			
		||||
                feedModels.addAll(result.getFeedModels());
 | 
			
		||||
 | 
			
		||||
                final List<Media> mediaResults = result.getFeedModels();
 | 
			
		||||
                feedModels.addAll(mediaResults);
 | 
			
		||||
 | 
			
		||||
                if (fetchListener != null) {
 | 
			
		||||
                    // if (feedModels.size() < 15 && hasNextPage) {
 | 
			
		||||
                    //     feedService.fetch(csrfToken, nextCursor, this);
 | 
			
		||||
 | 
			
		||||
@ -133,7 +133,7 @@ public final class PostFetcher extends AsyncTask<Void, Void, Media> {
 | 
			
		||||
                //     feedModelBuilder.setSliderItems(postModels);
 | 
			
		||||
                // }
 | 
			
		||||
                // return feedModelBuilder.build();
 | 
			
		||||
                return ResponseBodyUtils.parseGraphQLItem(media);
 | 
			
		||||
                return ResponseBodyUtils.parseGraphQLItem(media, null);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
//            if (logCollector != null) {
 | 
			
		||||
 | 
			
		||||
@ -1,101 +0,0 @@
 | 
			
		||||
package awais.instagrabber.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.interfaces.FetchListener;
 | 
			
		||||
import awais.instagrabber.repositories.responses.FriendshipStatus;
 | 
			
		||||
import awais.instagrabber.repositories.responses.User;
 | 
			
		||||
import awais.instagrabber.webservices.GraphQLService;
 | 
			
		||||
import awais.instagrabber.webservices.ServiceCallback;
 | 
			
		||||
import awais.instagrabber.webservices.UserService;
 | 
			
		||||
 | 
			
		||||
public final class ProfileFetcher extends AsyncTask<Void, Void, Void> {
 | 
			
		||||
    private static final String TAG = ProfileFetcher.class.getSimpleName();
 | 
			
		||||
    private final UserService userService;
 | 
			
		||||
    private final GraphQLService graphQLService;
 | 
			
		||||
 | 
			
		||||
    private final FetchListener<User> fetchListener;
 | 
			
		||||
    private final long myId;
 | 
			
		||||
    private final boolean isLoggedIn;
 | 
			
		||||
    private final String userName;
 | 
			
		||||
 | 
			
		||||
    public ProfileFetcher(final String userName,
 | 
			
		||||
                          final long myId,
 | 
			
		||||
                          final boolean isLoggedIn,
 | 
			
		||||
                          final FetchListener<User> fetchListener) {
 | 
			
		||||
        this.userName = userName;
 | 
			
		||||
        this.myId = myId;
 | 
			
		||||
        this.isLoggedIn = isLoggedIn;
 | 
			
		||||
        this.fetchListener = fetchListener;
 | 
			
		||||
        userService = isLoggedIn ? UserService.getInstance() : null;
 | 
			
		||||
        graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(final Void... voids) {
 | 
			
		||||
        if (isLoggedIn && userName != null) {
 | 
			
		||||
            userService.getUsernameInfo(userName, new ServiceCallback<User>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(final User user) {
 | 
			
		||||
                    userService.getUserFriendship(user.getPk(), new ServiceCallback<FriendshipStatus>() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void onSuccess(final FriendshipStatus status) {
 | 
			
		||||
                            user.setFriendshipStatus(status);
 | 
			
		||||
                            fetchListener.onResult(user);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void onFailure(final Throwable t) {
 | 
			
		||||
                            Log.e(TAG, "Error", t);
 | 
			
		||||
                            fetchListener.onFailure(t);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(final Throwable t) {
 | 
			
		||||
                    Log.e(TAG, "Error", t);
 | 
			
		||||
                    fetchListener.onFailure(t);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else if (isLoggedIn) {
 | 
			
		||||
            userService.getUserInfo(myId, new ServiceCallback<User>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(final User user) {
 | 
			
		||||
                    fetchListener.onResult(user);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(final Throwable t) {
 | 
			
		||||
                    Log.e(TAG, "Error", t);
 | 
			
		||||
                    fetchListener.onFailure(t);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            graphQLService.fetchUser(userName, new ServiceCallback<User>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(final User user) {
 | 
			
		||||
                    fetchListener.onResult(user);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(final Throwable t) {
 | 
			
		||||
                    Log.e(TAG, "Error", t);
 | 
			
		||||
                    fetchListener.onFailure(t);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPreExecute() {
 | 
			
		||||
        if (fetchListener != null) fetchListener.doBefore();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -49,7 +49,7 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService {
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        if (isLoggedIn) profileService.fetchPosts(profileModel.getPk(), nextMaxId, cb);
 | 
			
		||||
        else graphQLService.fetchProfilePosts(profileModel.getPk(), 30, nextMaxId, cb);
 | 
			
		||||
        else graphQLService.fetchProfilePosts(profileModel.getPk(), 30, nextMaxId, profileModel, cb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -32,11 +32,15 @@ import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;
 | 
			
		||||
import awais.instagrabber.interfaces.FetchListener;
 | 
			
		||||
import awais.instagrabber.models.PostsLayoutPreferences;
 | 
			
		||||
import awais.instagrabber.repositories.responses.Media;
 | 
			
		||||
import awais.instagrabber.utils.Constants;
 | 
			
		||||
import awais.instagrabber.utils.KeywordsFilterUtils;
 | 
			
		||||
import awais.instagrabber.utils.ResponseBodyUtils;
 | 
			
		||||
import awais.instagrabber.utils.Utils;
 | 
			
		||||
import awais.instagrabber.viewmodels.MediaViewModel;
 | 
			
		||||
import awais.instagrabber.workers.DownloadWorker;
 | 
			
		||||
 | 
			
		||||
import static awais.instagrabber.utils.Utils.settingsHelper;
 | 
			
		||||
 | 
			
		||||
public class PostsRecyclerView extends RecyclerView {
 | 
			
		||||
    private static final String TAG = "PostsRecyclerView";
 | 
			
		||||
 | 
			
		||||
@ -70,7 +74,13 @@ public class PostsRecyclerView extends RecyclerView {
 | 
			
		||||
            }
 | 
			
		||||
            final List<Media> models = mediaViewModel.getList().getValue();
 | 
			
		||||
            final List<Media> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
 | 
			
		||||
            modelsCopy.addAll(result);
 | 
			
		||||
            if (settingsHelper.getBoolean(Constants.TOGGLE_KEYWORD_FILTER)){
 | 
			
		||||
                final ArrayList<String> items = new ArrayList<>(settingsHelper.getStringSet(Constants.KEYWORD_FILTERS));
 | 
			
		||||
                modelsCopy.addAll(new KeywordsFilterUtils(items).filter(result));
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                modelsCopy.addAll(result);
 | 
			
		||||
            }
 | 
			
		||||
            mediaViewModel.getList().postValue(modelsCopy);
 | 
			
		||||
            dispatchFetchStatus();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ package awais.instagrabber.customviews.emoji;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
@ -12,10 +11,12 @@ public class Emoji {
 | 
			
		||||
    private final List<Emoji> variants;
 | 
			
		||||
    private GoogleCompatEmojiDrawable drawable;
 | 
			
		||||
 | 
			
		||||
    public Emoji(final String unicode, final String name) {
 | 
			
		||||
    public Emoji(final String unicode,
 | 
			
		||||
                 final String name,
 | 
			
		||||
                 final List<Emoji> variants) {
 | 
			
		||||
        this.unicode = unicode;
 | 
			
		||||
        this.name = name;
 | 
			
		||||
        this.variants = new LinkedList<>();
 | 
			
		||||
        this.variants = variants;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getUnicode() {
 | 
			
		||||
@ -35,7 +36,7 @@ public class Emoji {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public GoogleCompatEmojiDrawable getDrawable() {
 | 
			
		||||
        if (drawable == null) {
 | 
			
		||||
        if (drawable == null && unicode != null) {
 | 
			
		||||
            drawable = new GoogleCompatEmojiDrawable(unicode);
 | 
			
		||||
        }
 | 
			
		||||
        return drawable;
 | 
			
		||||
@ -60,6 +61,7 @@ public class Emoji {
 | 
			
		||||
        return "Emoji{" +
 | 
			
		||||
                "unicode='" + unicode + '\'' +
 | 
			
		||||
                ", name='" + name + '\'' +
 | 
			
		||||
                ", variants=" + variants +
 | 
			
		||||
                '}';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
package awais.instagrabber.customviews.emoji;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.DrawableRes;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
@ -10,12 +10,13 @@ import awais.instagrabber.R;
 | 
			
		||||
 | 
			
		||||
public class EmojiCategory {
 | 
			
		||||
    private final EmojiCategoryType type;
 | 
			
		||||
    private final Map<String, Emoji> emojis = new LinkedHashMap<>();
 | 
			
		||||
    private final Map<String, Emoji> emojis;
 | 
			
		||||
    @DrawableRes
 | 
			
		||||
    private int drawableRes;
 | 
			
		||||
 | 
			
		||||
    public EmojiCategory(final EmojiCategoryType type) {
 | 
			
		||||
    public EmojiCategory(final EmojiCategoryType type, final Map<String, Emoji> emojis) {
 | 
			
		||||
        this.type = type;
 | 
			
		||||
        this.emojis = emojis;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public EmojiCategoryType getType() {
 | 
			
		||||
@ -73,4 +74,13 @@ public class EmojiCategory {
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
        return Objects.hash(type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "EmojiCategory{" +
 | 
			
		||||
                "type=" + type +
 | 
			
		||||
                ", emojis=" + emojis +
 | 
			
		||||
                '}';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,78 @@
 | 
			
		||||
package awais.instagrabber.dialogs;
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.view.Window;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.Nullable;
 | 
			
		||||
import androidx.fragment.app.DialogFragment;
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager;
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.adapters.KeywordsFilterAdapter;
 | 
			
		||||
import awais.instagrabber.databinding.DialogKeywordsFilterBinding;
 | 
			
		||||
import awais.instagrabber.utils.Constants;
 | 
			
		||||
import awais.instagrabber.utils.SettingsHelper;
 | 
			
		||||
import awais.instagrabber.utils.Utils;
 | 
			
		||||
 | 
			
		||||
public final class KeywordsFilterDialog extends DialogFragment {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStart() {
 | 
			
		||||
        super.onStart();
 | 
			
		||||
        final Dialog dialog = getDialog();
 | 
			
		||||
        if (dialog == null) return;
 | 
			
		||||
        final Window window = dialog.getWindow();
 | 
			
		||||
        if (window == null) return;
 | 
			
		||||
        final int height = ViewGroup.LayoutParams.WRAP_CONTENT;
 | 
			
		||||
        final int width = (int) (Utils.displayMetrics.widthPixels * 0.8);
 | 
			
		||||
        window.setLayout(width, height);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 | 
			
		||||
        final DialogKeywordsFilterBinding dialogKeywordsFilterBinding = DialogKeywordsFilterBinding.inflate(inflater, container, false);
 | 
			
		||||
        init(dialogKeywordsFilterBinding, getContext());
 | 
			
		||||
        dialogKeywordsFilterBinding.btnOK.setOnClickListener(view -> this.dismiss());
 | 
			
		||||
        return dialogKeywordsFilterBinding.getRoot();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void init(DialogKeywordsFilterBinding dialogKeywordsFilterBinding, Context context){
 | 
			
		||||
        final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
 | 
			
		||||
        final RecyclerView recyclerView = dialogKeywordsFilterBinding.recyclerKeyword;
 | 
			
		||||
        recyclerView.setLayoutManager(linearLayoutManager);
 | 
			
		||||
 | 
			
		||||
        final SettingsHelper settingsHelper = new SettingsHelper(context);
 | 
			
		||||
        final ArrayList<String> items = new ArrayList<>(settingsHelper.getStringSet(Constants.KEYWORD_FILTERS));
 | 
			
		||||
        final KeywordsFilterAdapter adapter = new KeywordsFilterAdapter(context, items);
 | 
			
		||||
        recyclerView.setAdapter(adapter);
 | 
			
		||||
 | 
			
		||||
        final EditText editText = dialogKeywordsFilterBinding.editText;
 | 
			
		||||
 | 
			
		||||
        dialogKeywordsFilterBinding.btnAdd.setOnClickListener(view ->{
 | 
			
		||||
            final String s = editText.getText().toString();
 | 
			
		||||
            if(s.isEmpty()) return;
 | 
			
		||||
            if(items.contains(s)) {
 | 
			
		||||
                editText.setText("");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            items.add(s.toLowerCase());
 | 
			
		||||
            settingsHelper.putStringSet(Constants.KEYWORD_FILTERS, new HashSet<>(items));
 | 
			
		||||
            adapter.notifyItemInserted(items.size());
 | 
			
		||||
            final String message = context.getString(R.string.added_keywords, s);
 | 
			
		||||
            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
 | 
			
		||||
            editText.setText("");
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -378,6 +378,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
 | 
			
		||||
        if (getArguments() == null) return;
 | 
			
		||||
        final HashTagFragmentArgs fragmentArgs = HashTagFragmentArgs.fromBundle(getArguments());
 | 
			
		||||
        hashtag = fragmentArgs.getHashtag();
 | 
			
		||||
        if (hashtag.charAt(0) == '#') hashtag = hashtag.substring(1);
 | 
			
		||||
        fetchHashtagModel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -925,7 +925,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupLocation(final Location location) {
 | 
			
		||||
        if (location == null) {
 | 
			
		||||
        if (location == null || !detailsVisible) {
 | 
			
		||||
            binding.location.setVisibility(View.GONE);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@ import android.content.Context;
 | 
			
		||||
import android.content.DialogInterface;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.graphics.Typeface;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.os.Looper;
 | 
			
		||||
@ -57,7 +56,6 @@ import awais.instagrabber.activities.MainActivity;
 | 
			
		||||
import awais.instagrabber.adapters.FeedAdapterV2;
 | 
			
		||||
import awais.instagrabber.adapters.HighlightsAdapter;
 | 
			
		||||
import awais.instagrabber.asyncs.CreateThreadAction;
 | 
			
		||||
import awais.instagrabber.asyncs.ProfileFetcher;
 | 
			
		||||
import awais.instagrabber.asyncs.ProfilePostFetchService;
 | 
			
		||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
 | 
			
		||||
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
 | 
			
		||||
@ -73,7 +71,6 @@ import awais.instagrabber.db.repositories.RepositoryCallback;
 | 
			
		||||
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
 | 
			
		||||
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
 | 
			
		||||
import awais.instagrabber.fragments.PostViewV2Fragment;
 | 
			
		||||
import awais.instagrabber.interfaces.FetchListener;
 | 
			
		||||
import awais.instagrabber.managers.DirectMessagesManager;
 | 
			
		||||
import awais.instagrabber.managers.InboxManager;
 | 
			
		||||
import awais.instagrabber.models.HighlightModel;
 | 
			
		||||
@ -93,11 +90,14 @@ import awais.instagrabber.utils.CookieUtils;
 | 
			
		||||
import awais.instagrabber.utils.DownloadUtils;
 | 
			
		||||
import awais.instagrabber.utils.TextUtils;
 | 
			
		||||
import awais.instagrabber.utils.Utils;
 | 
			
		||||
import awais.instagrabber.viewmodels.AppStateViewModel;
 | 
			
		||||
import awais.instagrabber.viewmodels.HighlightsViewModel;
 | 
			
		||||
import awais.instagrabber.webservices.FriendshipService;
 | 
			
		||||
import awais.instagrabber.webservices.GraphQLService;
 | 
			
		||||
import awais.instagrabber.webservices.MediaService;
 | 
			
		||||
import awais.instagrabber.webservices.ServiceCallback;
 | 
			
		||||
import awais.instagrabber.webservices.StoriesService;
 | 
			
		||||
import awais.instagrabber.webservices.UserService;
 | 
			
		||||
 | 
			
		||||
import static androidx.core.content.PermissionChecker.checkSelfPermission;
 | 
			
		||||
import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG;
 | 
			
		||||
@ -120,6 +120,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
 | 
			
		||||
    private FriendshipService friendshipService;
 | 
			
		||||
    private StoriesService storiesService;
 | 
			
		||||
    private MediaService mediaService;
 | 
			
		||||
    private UserService userService;
 | 
			
		||||
    private GraphQLService graphQLService;
 | 
			
		||||
    private boolean shouldRefresh = true;
 | 
			
		||||
    private boolean hasStories = false;
 | 
			
		||||
    private HighlightsAdapter highlightsAdapter;
 | 
			
		||||
@ -304,6 +306,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
 | 
			
		||||
    private LayoutProfileDetailsBinding profileDetailsBinding;
 | 
			
		||||
    private AccountRepository accountRepository;
 | 
			
		||||
    private FavoriteRepository favoriteRepository;
 | 
			
		||||
    private AppStateViewModel appStateViewModel;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate(@Nullable final Bundle savedInstanceState) {
 | 
			
		||||
@ -317,8 +320,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
 | 
			
		||||
        friendshipService = isLoggedIn ? FriendshipService.getInstance(deviceUuid, csrfToken, myId) : null;
 | 
			
		||||
        storiesService = isLoggedIn ? StoriesService.getInstance(null, 0L, null) : null;
 | 
			
		||||
        mediaService = isLoggedIn ? MediaService.getInstance(null, null, 0) : null;
 | 
			
		||||
        userService = isLoggedIn ? UserService.getInstance() : null;
 | 
			
		||||
        graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
 | 
			
		||||
        accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext()));
 | 
			
		||||
        favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext()));
 | 
			
		||||
        appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);
 | 
			
		||||
        setHasOptionsMenu(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -601,31 +607,66 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
 | 
			
		||||
        if (usernameTemp.startsWith("@")) {
 | 
			
		||||
            usernameTemp = usernameTemp.substring(1);
 | 
			
		||||
        }
 | 
			
		||||
        new ProfileFetcher(TextUtils.isEmpty(username) ? null : usernameTemp, myId, isLoggedIn, new FetchListener<User>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onResult(final User user) {
 | 
			
		||||
                if (getContext() == null) return;
 | 
			
		||||
                if (TextUtils.isEmpty(username)) {
 | 
			
		||||
                    username = user.getUsername();
 | 
			
		||||
                    setUsernameDelayed();
 | 
			
		||||
        if (TextUtils.isEmpty(usernameTemp)) {
 | 
			
		||||
            profileModel = appStateViewModel.getCurrentUser();
 | 
			
		||||
            username = profileModel.getUsername();
 | 
			
		||||
            setUsernameDelayed();
 | 
			
		||||
            setProfileDetails();
 | 
			
		||||
        }
 | 
			
		||||
        else if (isLoggedIn) {
 | 
			
		||||
            userService.getUsernameInfo(usernameTemp, new ServiceCallback<User>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(final User user) {
 | 
			
		||||
                    userService.getUserFriendship(user.getPk(), new ServiceCallback<FriendshipStatus>() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void onSuccess(final FriendshipStatus status) {
 | 
			
		||||
                            user.setFriendshipStatus(status);
 | 
			
		||||
                            profileModel = user;
 | 
			
		||||
                            setProfileDetails();
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void onFailure(final Throwable t) {
 | 
			
		||||
                            Log.e(TAG, "Error fetching profile relationship", t);
 | 
			
		||||
                            final Context context = getContext();
 | 
			
		||||
                            try {
 | 
			
		||||
                                if (t == null) Toast.makeText(context, R.string.error_loading_profile_loggedin, Toast.LENGTH_LONG).show();
 | 
			
		||||
                                else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
 | 
			
		||||
                            } catch (final Throwable ignored) {}
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                profileModel = user;
 | 
			
		||||
                setProfileDetails();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(final Throwable t) {
 | 
			
		||||
                Log.e(TAG, "Error fetching profile", t);
 | 
			
		||||
                final Context context = getContext();
 | 
			
		||||
                try {
 | 
			
		||||
                    if (t == null) Toast.makeText(context,
 | 
			
		||||
                                                  isLoggedIn ? R.string.error_loading_profile_loggedin : R.string.error_loading_profile,
 | 
			
		||||
                                                  Toast.LENGTH_LONG).show();
 | 
			
		||||
                    else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
 | 
			
		||||
                } catch (final Throwable ignored) {}
 | 
			
		||||
            }
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(final Throwable t) {
 | 
			
		||||
                    Log.e(TAG, "Error fetching profile", t);
 | 
			
		||||
                    final Context context = getContext();
 | 
			
		||||
                    try {
 | 
			
		||||
                        if (t == null) Toast.makeText(context, R.string.error_loading_profile_loggedin, Toast.LENGTH_LONG).show();
 | 
			
		||||
                        else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    } catch (final Throwable ignored) {}
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            graphQLService.fetchUser(usernameTemp, new ServiceCallback<User>() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onSuccess(final User user) {
 | 
			
		||||
                    profileModel = user;
 | 
			
		||||
                    setProfileDetails();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onFailure(final Throwable t) {
 | 
			
		||||
                    Log.e(TAG, "Error fetching profile", t);
 | 
			
		||||
                    final Context context = getContext();
 | 
			
		||||
                    try {
 | 
			
		||||
                        if (t == null) Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_LONG).show();
 | 
			
		||||
                        else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    } catch (final Throwable ignored) {}
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setProfileDetails() {
 | 
			
		||||
 | 
			
		||||
@ -8,10 +8,9 @@ import androidx.preference.PreferenceScreen;
 | 
			
		||||
import androidx.preference.SwitchPreferenceCompat;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.R;
 | 
			
		||||
import awais.instagrabber.dialogs.KeywordsFilterDialog;
 | 
			
		||||
import awais.instagrabber.utils.Constants;
 | 
			
		||||
 | 
			
		||||
import static awais.instagrabber.utils.Utils.settingsHelper;
 | 
			
		||||
 | 
			
		||||
public class PostPreferencesFragment extends BasePreferencesFragment {
 | 
			
		||||
    @Override
 | 
			
		||||
    void setupPreferenceScreen(final PreferenceScreen screen) {
 | 
			
		||||
@ -20,6 +19,8 @@ public class PostPreferencesFragment extends BasePreferencesFragment {
 | 
			
		||||
        // generalCategory.addPreference(getAutoPlayVideosPreference(context));
 | 
			
		||||
        screen.addPreference(getAlwaysMuteVideosPreference(context));
 | 
			
		||||
        screen.addPreference(getShowCaptionPreference(context));
 | 
			
		||||
        screen.addPreference(getToggleKeywordFilterPreference(context));
 | 
			
		||||
        screen.addPreference(getEditKeywordFilterPreference(context));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Preference getAutoPlayVideosPreference(@NonNull final Context context) {
 | 
			
		||||
@ -46,4 +47,24 @@ public class PostPreferencesFragment extends BasePreferencesFragment {
 | 
			
		||||
        preference.setIconSpaceReserved(false);
 | 
			
		||||
        return preference;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Preference getToggleKeywordFilterPreference(@NonNull final Context context) {
 | 
			
		||||
        final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
 | 
			
		||||
        preference.setKey(Constants.TOGGLE_KEYWORD_FILTER);
 | 
			
		||||
        preference.setDefaultValue(false);
 | 
			
		||||
        preference.setTitle(R.string.toggle_keyword_filter);
 | 
			
		||||
        preference.setIconSpaceReserved(false);
 | 
			
		||||
        return preference;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Preference getEditKeywordFilterPreference(@NonNull final Context context){
 | 
			
		||||
        final Preference preference = new Preference(context);
 | 
			
		||||
        preference.setTitle(R.string.edit_keyword_filter);
 | 
			
		||||
        preference.setIconSpaceReserved(false);
 | 
			
		||||
        preference.setOnPreferenceClickListener(view ->{
 | 
			
		||||
            new KeywordsFilterDialog().show(getParentFragmentManager(), null);
 | 
			
		||||
            return true;
 | 
			
		||||
        });
 | 
			
		||||
        return preference;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,15 @@ public final class Constants {
 | 
			
		||||
    public static final String APP_THEME = "app_theme_v19";
 | 
			
		||||
    public static final String APP_LANGUAGE = "app_language_v19";
 | 
			
		||||
    public static final String STORY_SORT = "story_sort";
 | 
			
		||||
    // set string prefs
 | 
			
		||||
    public static final String KEYWORD_FILTERS = "keyword_filters";
 | 
			
		||||
    // int prefs, do not export
 | 
			
		||||
    public static final String PREV_INSTALL_VERSION = "prevVersion";
 | 
			
		||||
    public static final String BROWSER_UA_CODE = "browser_ua_code";
 | 
			
		||||
    public static final String APP_UA_CODE = "app_ua_code";
 | 
			
		||||
    // boolean prefs
 | 
			
		||||
    public static final String DOWNLOAD_USER_FOLDER = "download_user_folder";
 | 
			
		||||
    public static final String TOGGLE_KEYWORD_FILTER = "toggle_keyword_filter";
 | 
			
		||||
    // deprecated: public static final String BOTTOM_TOOLBAR = "bottom_toolbar";
 | 
			
		||||
    public static final String FOLDER_SAVE_TO = "saved_to";
 | 
			
		||||
    public static final String AUTOPLAY_VIDEOS = "autoplay_videos";
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,49 @@
 | 
			
		||||
package awais.instagrabber.utils;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.repositories.responses.Caption;
 | 
			
		||||
import awais.instagrabber.repositories.responses.Media;
 | 
			
		||||
 | 
			
		||||
public final class KeywordsFilterUtils {
 | 
			
		||||
 | 
			
		||||
    private final ArrayList<String> keywords;
 | 
			
		||||
 | 
			
		||||
    public KeywordsFilterUtils(final ArrayList<String> keywords){
 | 
			
		||||
        this.keywords = keywords;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean filter(final String caption){
 | 
			
		||||
        if(caption == null) return false;
 | 
			
		||||
        if(keywords.isEmpty()) return false;
 | 
			
		||||
        final String temp = caption.toLowerCase();
 | 
			
		||||
        for(final String s:keywords){
 | 
			
		||||
            if(temp.contains(s)) return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean filter(final Media media){
 | 
			
		||||
        if(media == null) return false;
 | 
			
		||||
        final Caption c = media.getCaption();
 | 
			
		||||
        if(c == null) return false;
 | 
			
		||||
        if(keywords.isEmpty()) return false;
 | 
			
		||||
        final String temp = c.getText().toLowerCase();
 | 
			
		||||
        for(final String s:keywords){
 | 
			
		||||
            if(temp.contains(s)) return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<Media> filter(final List<Media> media){
 | 
			
		||||
        if(keywords.isEmpty()) return media;
 | 
			
		||||
        if(media == null) return new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        final List<Media> result= new ArrayList<>();
 | 
			
		||||
        for(final Media m:media){
 | 
			
		||||
            if(!filter(m)) result.add(m);
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -675,7 +675,8 @@ public final class ResponseBodyUtils {
 | 
			
		||||
    //     return feedModelBuilder.build();
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    public static Media parseGraphQLItem(final JSONObject itemJson) throws JSONException {
 | 
			
		||||
    // the "user" argument can be null, it's used because instagram redacts user details from responses
 | 
			
		||||
    public static Media parseGraphQLItem(final JSONObject itemJson, final User backup) throws JSONException {
 | 
			
		||||
        if (itemJson == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
@ -728,41 +729,28 @@ public final class ResponseBodyUtils {
 | 
			
		||||
            width = dimensions.optInt("width");
 | 
			
		||||
        }
 | 
			
		||||
        String thumbnailUrl = null;
 | 
			
		||||
        try {
 | 
			
		||||
            thumbnailUrl = feedItem.getJSONArray("display_resources")
 | 
			
		||||
                                   .getJSONObject(0)
 | 
			
		||||
                                   .getString("src");
 | 
			
		||||
        } catch (JSONException ignored) {}
 | 
			
		||||
        // final FeedModel.Builder feedModelBuilder = new FeedModel.Builder()
 | 
			
		||||
        //         .setProfileModel(profileModel)
 | 
			
		||||
        //         .setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO
 | 
			
		||||
        //                              : MediaItemType.MEDIA_TYPE_IMAGE)
 | 
			
		||||
        //         .setViewCount(videoViews)
 | 
			
		||||
        //         .setPostId(feedItem.getString(Constants.EXTRAS_ID))
 | 
			
		||||
        //         .setDisplayUrl(resourceUrl)
 | 
			
		||||
        //         .setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl : displayUrl)
 | 
			
		||||
        //         .setShortCode(feedItem.getString(Constants.EXTRAS_SHORTCODE))
 | 
			
		||||
        //         .setPostCaption(captionText)
 | 
			
		||||
        //         .setCommentsCount(commentsCount)
 | 
			
		||||
        //         .setTimestamp(feedItem.optLong("taken_at_timestamp", -1))
 | 
			
		||||
        //         .setLiked(feedItem.optBoolean("viewer_has_liked"))
 | 
			
		||||
        //         .setBookmarked(feedItem.optBoolean("viewer_has_saved"))
 | 
			
		||||
        //         .setLikesCount(likesCount)
 | 
			
		||||
        //         .setLocationName(locationName)
 | 
			
		||||
        //         .setLocationId(String.valueOf(locationId))
 | 
			
		||||
        //         .setImageHeight(height)
 | 
			
		||||
        //         .setImageWidth(width);
 | 
			
		||||
        final JSONArray displayResources = feedItem.getJSONArray("display_resources");
 | 
			
		||||
        final List<MediaCandidate> candidates = new ArrayList<MediaCandidate>();
 | 
			
		||||
        for (int i = 0; i < displayResources.length(); i++) {
 | 
			
		||||
            final JSONObject displayResource = displayResources.getJSONObject(i);
 | 
			
		||||
            candidates.add(new MediaCandidate(
 | 
			
		||||
                    displayResource.getInt("config_width"),
 | 
			
		||||
                    displayResource.getInt("config_height"),
 | 
			
		||||
                    displayResource.getString("src")
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
        final ImageVersions2 imageVersions2 = new ImageVersions2(candidates);
 | 
			
		||||
 | 
			
		||||
        User user = null;
 | 
			
		||||
        User user = backup;
 | 
			
		||||
        long userId = -1;
 | 
			
		||||
        if (feedItem.has("owner")) {
 | 
			
		||||
        if (feedItem.has("owner") && user == null) {
 | 
			
		||||
            final JSONObject owner = feedItem.getJSONObject("owner");
 | 
			
		||||
            final FriendshipStatus friendshipStatus = new FriendshipStatus(
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
                    owner.optBoolean("is_private"),
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
@ -774,7 +762,7 @@ public final class ResponseBodyUtils {
 | 
			
		||||
                    userId,
 | 
			
		||||
                    owner.optString(Constants.EXTRAS_USERNAME),
 | 
			
		||||
                    owner.optString("full_name"),
 | 
			
		||||
                    owner.optBoolean("is_private"),
 | 
			
		||||
                    false,
 | 
			
		||||
                    owner.optString("profile_pic_url"),
 | 
			
		||||
                    null,
 | 
			
		||||
                    friendshipStatus,
 | 
			
		||||
@ -783,13 +771,6 @@ public final class ResponseBodyUtils {
 | 
			
		||||
                    null, null, null, null);
 | 
			
		||||
        }
 | 
			
		||||
        final String id = feedItem.getString(Constants.EXTRAS_ID);
 | 
			
		||||
        final ImageVersions2 imageVersions2 = new ImageVersions2(
 | 
			
		||||
                Collections.singletonList(new MediaCandidate(
 | 
			
		||||
                        width,
 | 
			
		||||
                        height,
 | 
			
		||||
                        isVideo ? thumbnailUrl : resourceUrl
 | 
			
		||||
                ))
 | 
			
		||||
        );
 | 
			
		||||
        VideoVersion videoVersion = null;
 | 
			
		||||
        if (isVideo) {
 | 
			
		||||
            videoVersion = new VideoVersion(
 | 
			
		||||
@ -821,7 +802,7 @@ public final class ResponseBodyUtils {
 | 
			
		||||
                    for (int i = 0; i < children.length(); i++) {
 | 
			
		||||
                        final JSONObject child = children.optJSONObject(i);
 | 
			
		||||
                        if (child == null) continue;
 | 
			
		||||
                        final Media media = parseGraphQLItem(child);
 | 
			
		||||
                        final Media media = parseGraphQLItem(child, null);
 | 
			
		||||
                        media.setIsSidecarChild(true);
 | 
			
		||||
                        childItems.add(media);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,9 @@ import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.annotation.StringDef;
 | 
			
		||||
import androidx.appcompat.app.AppCompatDelegate;
 | 
			
		||||
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH;
 | 
			
		||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER;
 | 
			
		||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT;
 | 
			
		||||
@ -39,6 +42,7 @@ import static awais.instagrabber.utils.Constants.MUTED_VIDEOS;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.PREF_DARK_THEME;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.PREF_EMOJI_VARIANTS;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.PREF_HASHTAG_POSTS_LAYOUT;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.KEYWORD_FILTERS;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.PREF_LIGHT_THEME;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.PREF_LIKED_POSTS_LAYOUT;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.PREF_LOCATION_POSTS_LAYOUT;
 | 
			
		||||
@ -54,6 +58,7 @@ import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.SKIPPED_VERSION;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.STORY_SORT;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.SWAP_DATE_TIME_FORMAT_ENABLED;
 | 
			
		||||
import static awais.instagrabber.utils.Constants.TOGGLE_KEYWORD_FILTER;
 | 
			
		||||
 | 
			
		||||
public final class SettingsHelper {
 | 
			
		||||
    private final SharedPreferences sharedPreferences;
 | 
			
		||||
@ -69,6 +74,12 @@ public final class SettingsHelper {
 | 
			
		||||
        return stringDefault;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Set<String> getStringSet(@StringSetSettings final String key) {
 | 
			
		||||
        final Set<String> stringSetDefault = new HashSet<>();
 | 
			
		||||
        if (sharedPreferences != null) return sharedPreferences.getStringSet(key, stringSetDefault);
 | 
			
		||||
        return stringSetDefault;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getInteger(@IntegerSettings final String key) {
 | 
			
		||||
        final int integerDefault = getIntegerDefault(key);
 | 
			
		||||
        if (sharedPreferences != null) return sharedPreferences.getInt(key, integerDefault);
 | 
			
		||||
@ -123,6 +134,10 @@ public final class SettingsHelper {
 | 
			
		||||
        if (sharedPreferences != null) sharedPreferences.edit().putString(key, val).apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void putStringSet(@StringSetSettings final String key, final Set<String> val) {
 | 
			
		||||
        if (sharedPreferences != null) sharedPreferences.edit().putStringSet(key, val).apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void putInteger(@IntegerSettings final String key, final int val) {
 | 
			
		||||
        if (sharedPreferences != null) sharedPreferences.edit().putInt(key, val).apply();
 | 
			
		||||
    }
 | 
			
		||||
@ -146,9 +161,12 @@ public final class SettingsHelper {
 | 
			
		||||
    @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
 | 
			
		||||
                       SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY,
 | 
			
		||||
                       CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH,
 | 
			
		||||
                       FLAG_SECURE, PREF_ENABLE_SENTRY})
 | 
			
		||||
                       FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY})
 | 
			
		||||
    public @interface BooleanSettings {}
 | 
			
		||||
 | 
			
		||||
    @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER})
 | 
			
		||||
    public @interface IntegerSettings {}
 | 
			
		||||
 | 
			
		||||
    @StringDef({KEYWORD_FILTERS})
 | 
			
		||||
    public @interface StringSetSettings {}
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
package awais.instagrabber.utils.emoji;
 | 
			
		||||
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.JsonDeserializationContext;
 | 
			
		||||
import com.google.gson.JsonDeserializer;
 | 
			
		||||
import com.google.gson.JsonElement;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
import com.google.gson.JsonParseException;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Type;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.customviews.emoji.Emoji;
 | 
			
		||||
import awais.instagrabber.customviews.emoji.EmojiCategory;
 | 
			
		||||
import awais.instagrabber.customviews.emoji.EmojiCategoryType;
 | 
			
		||||
 | 
			
		||||
public class EmojiCategoryDeserializer implements JsonDeserializer<EmojiCategory> {
 | 
			
		||||
    private static final String TAG = EmojiCategoryDeserializer.class.getSimpleName();
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public EmojiCategory deserialize(final JsonElement json,
 | 
			
		||||
                                     final Type typeOfT,
 | 
			
		||||
                                     final JsonDeserializationContext context) throws JsonParseException {
 | 
			
		||||
        final JsonObject jsonObject = json.getAsJsonObject();
 | 
			
		||||
        final JsonElement typeElement = jsonObject.get("type");
 | 
			
		||||
        final JsonObject emojisObject = jsonObject.getAsJsonObject("emojis");
 | 
			
		||||
        if (typeElement == null || emojisObject == null) {
 | 
			
		||||
            throw new JsonParseException("Invalid json for EmojiCategory");
 | 
			
		||||
        }
 | 
			
		||||
        final String typeString = typeElement.getAsString();
 | 
			
		||||
        EmojiCategoryType type;
 | 
			
		||||
        try {
 | 
			
		||||
            type = EmojiCategoryType.valueOf(typeString);
 | 
			
		||||
        } catch (IllegalArgumentException e) {
 | 
			
		||||
            Log.e(TAG, "deserialize: ", e);
 | 
			
		||||
            type = EmojiCategoryType.OTHERS;
 | 
			
		||||
        }
 | 
			
		||||
        final Map<String, Emoji> emojis = new LinkedHashMap<>();
 | 
			
		||||
        for (final Map.Entry<String, JsonElement> emojiObjectEntry : emojisObject.entrySet()) {
 | 
			
		||||
            final String unicode = emojiObjectEntry.getKey();
 | 
			
		||||
            final JsonElement value = emojiObjectEntry.getValue();
 | 
			
		||||
            if (unicode == null || value == null) {
 | 
			
		||||
                throw new JsonParseException("Invalid json for EmojiCategory");
 | 
			
		||||
            }
 | 
			
		||||
            final Emoji emoji = context.deserialize(value, Emoji.class);
 | 
			
		||||
            emojis.put(unicode, emoji);
 | 
			
		||||
        }
 | 
			
		||||
        return new EmojiCategory(type, emojis);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,44 @@
 | 
			
		||||
package awais.instagrabber.utils.emoji;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.JsonArray;
 | 
			
		||||
import com.google.gson.JsonDeserializationContext;
 | 
			
		||||
import com.google.gson.JsonDeserializer;
 | 
			
		||||
import com.google.gson.JsonElement;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
import com.google.gson.JsonParseException;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Type;
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.customviews.emoji.Emoji;
 | 
			
		||||
 | 
			
		||||
public class EmojiDeserializer implements JsonDeserializer<Emoji> {
 | 
			
		||||
    @Override
 | 
			
		||||
    public Emoji deserialize(final JsonElement json,
 | 
			
		||||
                             final Type typeOfT,
 | 
			
		||||
                             final JsonDeserializationContext context) throws JsonParseException {
 | 
			
		||||
        final JsonObject jsonObject = json.getAsJsonObject();
 | 
			
		||||
        final JsonElement unicodeElement = jsonObject.get("unicode");
 | 
			
		||||
        final JsonElement nameElement = jsonObject.get("name");
 | 
			
		||||
        if (unicodeElement == null || nameElement == null) {
 | 
			
		||||
            throw new JsonParseException("Invalid json for Emoji class");
 | 
			
		||||
        }
 | 
			
		||||
        final JsonElement variantsElement = jsonObject.get("variants");
 | 
			
		||||
        final List<Emoji> variants = new LinkedList<>();
 | 
			
		||||
        if (variantsElement != null) {
 | 
			
		||||
            final JsonArray variantsArray = variantsElement.getAsJsonArray();
 | 
			
		||||
            for (final JsonElement variantElement : variantsArray) {
 | 
			
		||||
                final Emoji variant = context.deserialize(variantElement, Emoji.class);
 | 
			
		||||
                if (variant != null) {
 | 
			
		||||
                    variants.add(variant);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return new Emoji(
 | 
			
		||||
                unicodeElement.getAsString(),
 | 
			
		||||
                nameElement.getAsString(),
 | 
			
		||||
                variants
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -3,10 +3,11 @@ package awais.instagrabber.utils.emoji;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.gson.FieldNamingPolicy;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import com.google.gson.GsonBuilder;
 | 
			
		||||
import com.google.gson.reflect.TypeToken;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.lang.reflect.Type;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
@ -52,7 +53,12 @@ public final class EmojiParser {
 | 
			
		||||
        }
 | 
			
		||||
        try (final InputStream in = classLoader.getResourceAsStream(file)) {
 | 
			
		||||
            final String json = NetworkUtils.readFromInputStream(in);
 | 
			
		||||
            final Gson gson = new Gson();
 | 
			
		||||
            final Gson gson = new GsonBuilder()
 | 
			
		||||
                    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
 | 
			
		||||
                    .registerTypeAdapter(EmojiCategory.class, new EmojiCategoryDeserializer())
 | 
			
		||||
                    .registerTypeAdapter(Emoji.class, new EmojiDeserializer())
 | 
			
		||||
                    .setLenient()
 | 
			
		||||
                    .create();
 | 
			
		||||
            final Type type = new TypeToken<Map<EmojiCategoryType, EmojiCategory>>() {}.getType();
 | 
			
		||||
            categoryMap = gson.fromJson(json, type);
 | 
			
		||||
            // Log.d(TAG, "EmojiParser: " + categoryMap);
 | 
			
		||||
@ -68,7 +74,7 @@ public final class EmojiParser {
 | 
			
		||||
                                           .build()
 | 
			
		||||
                                           .stream())
 | 
			
		||||
                                   .collect(Collectors.toMap(Emoji::getUnicode, Function.identity()));
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.e(TAG, "EmojiParser: ", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,18 @@
 | 
			
		||||
package awais.instagrabber.viewmodels;
 | 
			
		||||
 | 
			
		||||
import android.app.Application;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
import androidx.lifecycle.AndroidViewModel;
 | 
			
		||||
 | 
			
		||||
import awais.instagrabber.asyncs.ProfileFetcher;
 | 
			
		||||
import awais.instagrabber.db.datasources.AccountDataSource;
 | 
			
		||||
import awais.instagrabber.db.entities.Account;
 | 
			
		||||
import awais.instagrabber.db.repositories.AccountRepository;
 | 
			
		||||
import awais.instagrabber.db.repositories.RepositoryCallback;
 | 
			
		||||
import awais.instagrabber.interfaces.FetchListener;
 | 
			
		||||
import awais.instagrabber.repositories.responses.User;
 | 
			
		||||
import awais.instagrabber.utils.Constants;
 | 
			
		||||
import awais.instagrabber.utils.CookieUtils;
 | 
			
		||||
import awais.instagrabber.utils.TextUtils;
 | 
			
		||||
import awais.instagrabber.webservices.ServiceCallback;
 | 
			
		||||
import awais.instagrabber.webservices.UserService;
 | 
			
		||||
 | 
			
		||||
import static awais.instagrabber.utils.Utils.settingsHelper;
 | 
			
		||||
 | 
			
		||||
@ -27,6 +24,7 @@ public class AppStateViewModel extends AndroidViewModel {
 | 
			
		||||
 | 
			
		||||
    private User currentUser;
 | 
			
		||||
    private AccountRepository accountRepository;
 | 
			
		||||
    private UserService userService;
 | 
			
		||||
 | 
			
		||||
    public AppStateViewModel(@NonNull final Application application) {
 | 
			
		||||
        super(application);
 | 
			
		||||
@ -34,6 +32,7 @@ public class AppStateViewModel extends AndroidViewModel {
 | 
			
		||||
        cookie = settingsHelper.getString(Constants.COOKIE);
 | 
			
		||||
        isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
 | 
			
		||||
        if (!isLoggedIn) return;
 | 
			
		||||
        userService = UserService.getInstance();
 | 
			
		||||
        accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(application));
 | 
			
		||||
        fetchProfileDetails();
 | 
			
		||||
    }
 | 
			
		||||
@ -44,6 +43,14 @@ public class AppStateViewModel extends AndroidViewModel {
 | 
			
		||||
 | 
			
		||||
    private void fetchProfileDetails() {
 | 
			
		||||
        final long uid = CookieUtils.getUserIdFromCookie(cookie);
 | 
			
		||||
        new ProfileFetcher(null, uid, true, user -> this.currentUser = user).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 | 
			
		||||
        userService.getUserInfo(uid, new ServiceCallback<User>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(final User user) {
 | 
			
		||||
                currentUser = user;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(final Throwable t) {}
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,7 @@ public class GraphQLService extends BaseService {
 | 
			
		||||
                       final String variables,
 | 
			
		||||
                       final String arg1,
 | 
			
		||||
                       final String arg2,
 | 
			
		||||
                       final User backup,
 | 
			
		||||
                       final ServiceCallback<PostsFetchResponse> callback) {
 | 
			
		||||
        final Map<String, String> queryMap = new HashMap<>();
 | 
			
		||||
        queryMap.put("query_hash", queryHash);
 | 
			
		||||
@ -66,7 +67,7 @@ public class GraphQLService extends BaseService {
 | 
			
		||||
            public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
 | 
			
		||||
                try {
 | 
			
		||||
                    // Log.d(TAG, "onResponse: body: " + response.body());
 | 
			
		||||
                    final PostsFetchResponse postsFetchResponse = parsePostResponse(response, arg1, arg2);
 | 
			
		||||
                    final PostsFetchResponse postsFetchResponse = parsePostResponse(response, arg1, arg2, backup);
 | 
			
		||||
                    if (callback != null) {
 | 
			
		||||
                        callback.onSuccess(postsFetchResponse);
 | 
			
		||||
                    }
 | 
			
		||||
@ -96,6 +97,7 @@ public class GraphQLService extends BaseService {
 | 
			
		||||
                      "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}",
 | 
			
		||||
              Constants.EXTRAS_LOCATION,
 | 
			
		||||
              "edge_location_to_media",
 | 
			
		||||
              null,
 | 
			
		||||
              callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -108,12 +110,14 @@ public class GraphQLService extends BaseService {
 | 
			
		||||
                      "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}",
 | 
			
		||||
              Constants.EXTRAS_HASHTAG,
 | 
			
		||||
              "edge_hashtag_to_media",
 | 
			
		||||
              null,
 | 
			
		||||
              callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void fetchProfilePosts(final long profileId,
 | 
			
		||||
                                  final int postsPerPage,
 | 
			
		||||
                                  final String maxId,
 | 
			
		||||
                                  final User backup,
 | 
			
		||||
                                  final ServiceCallback<PostsFetchResponse> callback) {
 | 
			
		||||
        fetch("18a7b935ab438c4514b1f742d8fa07a7",
 | 
			
		||||
              "{\"id\":\"" + profileId + "\"," +
 | 
			
		||||
@ -121,6 +125,7 @@ public class GraphQLService extends BaseService {
 | 
			
		||||
                      "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}",
 | 
			
		||||
              Constants.EXTRAS_USER,
 | 
			
		||||
              "edge_owner_to_timeline_media",
 | 
			
		||||
              backup,
 | 
			
		||||
              callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -134,21 +139,28 @@ public class GraphQLService extends BaseService {
 | 
			
		||||
                      "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}",
 | 
			
		||||
              Constants.EXTRAS_USER,
 | 
			
		||||
              "edge_user_to_photos_of_you",
 | 
			
		||||
              null,
 | 
			
		||||
              callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private PostsFetchResponse parsePostResponse(@NonNull final Response<String> response, @NonNull final String arg1, @NonNull final String arg2)
 | 
			
		||||
    private PostsFetchResponse parsePostResponse(@NonNull final Response<String> response,
 | 
			
		||||
                                                 @NonNull final String arg1,
 | 
			
		||||
                                                 @NonNull final String arg2,
 | 
			
		||||
                                                 final User backup)
 | 
			
		||||
            throws JSONException {
 | 
			
		||||
        if (TextUtils.isEmpty(response.body())) {
 | 
			
		||||
            Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code());
 | 
			
		||||
            return new PostsFetchResponse(Collections.emptyList(), false, null);
 | 
			
		||||
        }
 | 
			
		||||
        return parseResponseBody(response.body(), arg1, arg2);
 | 
			
		||||
        return parseResponseBody(response.body(), arg1, arg2, backup);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @NonNull
 | 
			
		||||
    private PostsFetchResponse parseResponseBody(@NonNull final String body, @NonNull final String arg1, @NonNull final String arg2)
 | 
			
		||||
    private PostsFetchResponse parseResponseBody(@NonNull final String body,
 | 
			
		||||
                                                 @NonNull final String arg1,
 | 
			
		||||
                                                 @NonNull final String arg2,
 | 
			
		||||
                                                 final User backup)
 | 
			
		||||
            throws JSONException {
 | 
			
		||||
        final List<Media> items = new ArrayList<>();
 | 
			
		||||
        final JSONObject timelineFeed = new JSONObject(body)
 | 
			
		||||
@ -174,7 +186,7 @@ public class GraphQLService extends BaseService {
 | 
			
		||||
            if (itemJson == null) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            final Media media = ResponseBodyUtils.parseGraphQLItem(itemJson);
 | 
			
		||||
            final Media media = ResponseBodyUtils.parseGraphQLItem(itemJson, backup);
 | 
			
		||||
            if (media != null) {
 | 
			
		||||
                items.add(media);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -121,9 +121,15 @@ public class NewsService extends BaseService {
 | 
			
		||||
                    callback.onSuccess(null);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                final List<AymlUser> aymlUsers = new ArrayList<>();
 | 
			
		||||
                aymlUsers.addAll(body.getNewSuggestedUsers().getSuggestions());
 | 
			
		||||
                aymlUsers.addAll(body.getSuggestedUsers().getSuggestions());
 | 
			
		||||
                final List<AymlUser> aymlUsers = new ArrayList<AymlUser>();
 | 
			
		||||
                final List<AymlUser> newSuggestions = body.getNewSuggestedUsers().getSuggestions();
 | 
			
		||||
                if (newSuggestions != null) {
 | 
			
		||||
                    aymlUsers.addAll(newSuggestions);
 | 
			
		||||
                }
 | 
			
		||||
                final List<AymlUser> oldSuggestions = body.getSuggestedUsers().getSuggestions();
 | 
			
		||||
                if (oldSuggestions != null) {
 | 
			
		||||
                    aymlUsers.addAll(oldSuggestions);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                final List<Notification> newsItems = aymlUsers.stream()
 | 
			
		||||
                        .map(i -> {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								app/src/main/res/layout/dialog_keywords_filter.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								app/src/main/res/layout/dialog_keywords_filter.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<ScrollView 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:padding="16dp">
 | 
			
		||||
 | 
			
		||||
    <androidx.constraintlayout.widget.ConstraintLayout
 | 
			
		||||
        android:id="@+id/root"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:animateLayoutChanges="true"
 | 
			
		||||
        android:paddingTop="16dp">
 | 
			
		||||
 | 
			
		||||
        <androidx.appcompat.widget.AppCompatEditText
 | 
			
		||||
            android:id="@+id/edit_text"
 | 
			
		||||
            android:layout_width="0dp"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:hint="@string/hint_keyword"
 | 
			
		||||
            android:singleLine="true"
 | 
			
		||||
            app:layout_constraintEnd_toStartOf="@id/btnAdd"
 | 
			
		||||
            app:layout_constraintStart_toStartOf="parent"
 | 
			
		||||
            app:layout_constraintTop_toTopOf="parent" />
 | 
			
		||||
 | 
			
		||||
        <Button
 | 
			
		||||
            android:id="@+id/btnAdd"
 | 
			
		||||
            android:layout_width="30dp"
 | 
			
		||||
            android:layout_height="30dp"
 | 
			
		||||
            android:background="@drawable/ic_add"
 | 
			
		||||
            app:layout_constraintEnd_toEndOf="parent"
 | 
			
		||||
            app:layout_constraintStart_toEndOf="@id/edit_text"
 | 
			
		||||
            app:layout_constraintTop_toTopOf="parent"
 | 
			
		||||
            app:layout_constraintBottom_toBottomOf="@id/edit_text" />
 | 
			
		||||
 | 
			
		||||
        <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
            android:id="@+id/recyclerKeyword"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            app:layout_constraintTop_toBottomOf="@id/btnAdd"
 | 
			
		||||
            app:layout_constraintStart_toStartOf="parent"
 | 
			
		||||
            app:layout_constraintEnd_toEndOf="parent"
 | 
			
		||||
            tools:layout_editor_absoluteX="16dp"
 | 
			
		||||
            tools:listitem="@layout/item_keyword" />
 | 
			
		||||
 | 
			
		||||
        <androidx.appcompat.widget.AppCompatButton
 | 
			
		||||
            android:id="@+id/btnOK"
 | 
			
		||||
            style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:layout_gravity="end"
 | 
			
		||||
            android:text="@string/ok"
 | 
			
		||||
            app:layout_constraintEnd_toEndOf="parent"
 | 
			
		||||
            app:layout_constraintTop_toBottomOf="@id/recyclerKeyword" />
 | 
			
		||||
 | 
			
		||||
    </androidx.constraintlayout.widget.ConstraintLayout>
 | 
			
		||||
</ScrollView>
 | 
			
		||||
							
								
								
									
										23
									
								
								app/src/main/res/layout/item_keyword.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/src/main/res/layout/item_keyword.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="wrap_content"
 | 
			
		||||
    android:gravity="center_vertical"
 | 
			
		||||
    android:orientation="horizontal"
 | 
			
		||||
    android:paddingBottom="8dp"
 | 
			
		||||
    android:paddingTop="8dp">
 | 
			
		||||
 | 
			
		||||
    <androidx.appcompat.widget.AppCompatTextView
 | 
			
		||||
        android:id="@+id/keyword_text"
 | 
			
		||||
        android:layout_width="0dp"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_weight="1" />
 | 
			
		||||
 | 
			
		||||
    <Button
 | 
			
		||||
        android:id="@+id/keyword_delete"
 | 
			
		||||
        android:layout_width="30dp"
 | 
			
		||||
        android:layout_height="30dp"
 | 
			
		||||
        android:background="@drawable/ic_delete"
 | 
			
		||||
        android:scaleType="center" />
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
@ -466,6 +466,11 @@
 | 
			
		||||
    <string name="generic_null_response">Response is null!</string>
 | 
			
		||||
    <string name="generic_not_ok_response">Response status is not ok!</string>
 | 
			
		||||
    <string name="generic_failed_request">Request failed!</string>
 | 
			
		||||
    <string name="hint_keyword">Keyword</string>
 | 
			
		||||
    <string name="toggle_keyword_filter">Enable keyword filter</string>
 | 
			
		||||
    <string name="edit_keyword_filter">Edit keyword filters</string>
 | 
			
		||||
    <string name="added_keywords">Added keyword: %s to filter list</string>
 | 
			
		||||
    <string name="removed_keywords">Removed keyword: %s from filter list</string>
 | 
			
		||||
    <string name="marked_as_seen">Marked as seen</string>
 | 
			
		||||
    <string name="delete_unsuccessful">Delete unsuccessful</string>
 | 
			
		||||
    <string name="crash_report_subject">Barinsta Crash Report</string>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user