mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-11-04 13:35:36 +00:00 
			
		
		
		
	Approve/Deny new users
This commit is contained in:
		
							parent
							
								
									6aacf1945f
								
							
						
					
					
						commit
						7addb16e5c
					
				@ -700,4 +700,8 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
 | 
				
			|||||||
              });
 | 
					              });
 | 
				
			||||||
        EmojiCompat.init(config);
 | 
					        EmojiCompat.init(config);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Toolbar getToolbar() {
 | 
				
			||||||
 | 
					        return binding.toolbar;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					package awais.instagrabber.adapters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.view.LayoutInflater;
 | 
				
			||||||
 | 
					import android.view.ViewGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.NonNull;
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.DiffUtil;
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.ListAdapter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import awais.instagrabber.adapters.viewholder.directmessages.DirectPendingUserViewHolder;
 | 
				
			||||||
 | 
					import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.User;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public final class DirectPendingUsersAdapter extends ListAdapter<DirectPendingUsersAdapter.PendingUser, DirectPendingUserViewHolder> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final DiffUtil.ItemCallback<PendingUser> DIFF_CALLBACK = new DiffUtil.ItemCallback<PendingUser>() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public boolean areItemsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
 | 
				
			||||||
 | 
					            return oldItem.user.getPk() == newItem.user.getPk();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public boolean areContentsTheSame(@NonNull final PendingUser oldItem, @NonNull final PendingUser newItem) {
 | 
				
			||||||
 | 
					            return Objects.equals(oldItem.user.getUsername(), newItem.user.getUsername()) &&
 | 
				
			||||||
 | 
					                    Objects.equals(oldItem.user.getFullName(), newItem.user.getFullName()) &&
 | 
				
			||||||
 | 
					                    Objects.equals(oldItem.requester, newItem.requester);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final PendingUserCallback callback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DirectPendingUsersAdapter(final PendingUserCallback callback) {
 | 
				
			||||||
 | 
					        super(DIFF_CALLBACK);
 | 
				
			||||||
 | 
					        this.callback = callback;
 | 
				
			||||||
 | 
					        setHasStableIds(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void submitPendingRequests(final DirectThreadParticipantRequestsResponse requests) {
 | 
				
			||||||
 | 
					        if (requests == null || requests.getUsers() == null) {
 | 
				
			||||||
 | 
					            submitList(Collections.emptyList());
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        submitList(parse(requests));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private List<PendingUser> parse(final DirectThreadParticipantRequestsResponse requests) {
 | 
				
			||||||
 | 
					        final List<User> users = requests.getUsers();
 | 
				
			||||||
 | 
					        final Map<Long, String> requesterUsernames = requests.getRequesterUsernames();
 | 
				
			||||||
 | 
					        return users.stream()
 | 
				
			||||||
 | 
					                    .map(user -> new PendingUser(user, requesterUsernames.get(user.getPk())))
 | 
				
			||||||
 | 
					                    .collect(Collectors.toList());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public DirectPendingUserViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
 | 
				
			||||||
 | 
					        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
 | 
				
			||||||
 | 
					        final LayoutDmPendingUserItemBinding binding = LayoutDmPendingUserItemBinding.inflate(layoutInflater, parent, false);
 | 
				
			||||||
 | 
					        return new DirectPendingUserViewHolder(binding, callback);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onBindViewHolder(@NonNull final DirectPendingUserViewHolder holder, final int position) {
 | 
				
			||||||
 | 
					        final PendingUser pendingUser = getItem(position);
 | 
				
			||||||
 | 
					        holder.bind(position, pendingUser);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long getItemId(final int position) {
 | 
				
			||||||
 | 
					        final PendingUser item = getItem(position);
 | 
				
			||||||
 | 
					        return item.user.getPk();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class PendingUser {
 | 
				
			||||||
 | 
					        private final User user;
 | 
				
			||||||
 | 
					        private final String requester;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private boolean inProgress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public PendingUser(final User user, final String requester) {
 | 
				
			||||||
 | 
					            this.user = user;
 | 
				
			||||||
 | 
					            this.requester = requester;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public User getUser() {
 | 
				
			||||||
 | 
					            return user;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String getRequester() {
 | 
				
			||||||
 | 
					            return requester;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean isInProgress() {
 | 
				
			||||||
 | 
					            return inProgress;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public PendingUser setInProgress(final boolean inProgress) {
 | 
				
			||||||
 | 
					            this.inProgress = inProgress;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public interface PendingUserCallback {
 | 
				
			||||||
 | 
					        void onClick(int position, PendingUser pendingUser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void onApprove(int position, PendingUser pendingUser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void onDeny(int position, PendingUser pendingUser);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					package awais.instagrabber.adapters.viewholder.directmessages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.graphics.drawable.Drawable;
 | 
				
			||||||
 | 
					import android.text.SpannableStringBuilder;
 | 
				
			||||||
 | 
					import android.text.Spanned;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.NonNull;
 | 
				
			||||||
 | 
					import androidx.appcompat.content.res.AppCompatResources;
 | 
				
			||||||
 | 
					import androidx.recyclerview.widget.RecyclerView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import awais.instagrabber.R;
 | 
				
			||||||
 | 
					import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;
 | 
				
			||||||
 | 
					import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;
 | 
				
			||||||
 | 
					import awais.instagrabber.customviews.VerticalImageSpan;
 | 
				
			||||||
 | 
					import awais.instagrabber.databinding.LayoutDmPendingUserItemBinding;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.User;
 | 
				
			||||||
 | 
					import awais.instagrabber.utils.Utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DirectPendingUserViewHolder extends RecyclerView.ViewHolder {
 | 
				
			||||||
 | 
					    private static final String TAG = DirectPendingUserViewHolder.class.getSimpleName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final LayoutDmPendingUserItemBinding binding;
 | 
				
			||||||
 | 
					    private final PendingUserCallback callback;
 | 
				
			||||||
 | 
					    private final int drawableSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private VerticalImageSpan verifiedSpan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DirectPendingUserViewHolder(@NonNull final LayoutDmPendingUserItemBinding binding,
 | 
				
			||||||
 | 
					                                       final PendingUserCallback callback) {
 | 
				
			||||||
 | 
					        super(binding.getRoot());
 | 
				
			||||||
 | 
					        this.binding = binding;
 | 
				
			||||||
 | 
					        this.callback = callback;
 | 
				
			||||||
 | 
					        drawableSize = Utils.convertDpToPx(24);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void bind(final int position, final PendingUser pendingUser) {
 | 
				
			||||||
 | 
					        if (pendingUser == null) return;
 | 
				
			||||||
 | 
					        binding.getRoot().setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (callback == null) return;
 | 
				
			||||||
 | 
					            callback.onClick(position, pendingUser);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        setUsername(pendingUser);
 | 
				
			||||||
 | 
					        binding.requester.setText(itemView.getResources().getString(R.string.added_by, pendingUser.getRequester()));
 | 
				
			||||||
 | 
					        binding.profilePic.setImageURI(pendingUser.getUser().getProfilePicUrl());
 | 
				
			||||||
 | 
					        if (pendingUser.isInProgress()) {
 | 
				
			||||||
 | 
					            binding.approve.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            binding.deny.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            binding.progress.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        binding.approve.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					        binding.deny.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					        binding.progress.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					        binding.approve.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (callback == null) return;
 | 
				
			||||||
 | 
					            callback.onApprove(position, pendingUser);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        binding.deny.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (callback == null) return;
 | 
				
			||||||
 | 
					            callback.onDeny(position, pendingUser);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void setUsername(final PendingUser pendingUser) {
 | 
				
			||||||
 | 
					        final User user = pendingUser.getUser();
 | 
				
			||||||
 | 
					        final SpannableStringBuilder sb = new SpannableStringBuilder(user.getUsername());
 | 
				
			||||||
 | 
					        if (user.isVerified()) {
 | 
				
			||||||
 | 
					            if (verifiedSpan == null) {
 | 
				
			||||||
 | 
					                final Drawable verifiedDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.verified);
 | 
				
			||||||
 | 
					                if (verifiedDrawable != null) {
 | 
				
			||||||
 | 
					                    final Drawable drawable = verifiedDrawable.mutate();
 | 
				
			||||||
 | 
					                    drawable.setBounds(0, 0, drawableSize, drawableSize);
 | 
				
			||||||
 | 
					                    verifiedSpan = new VerticalImageSpan(drawable);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                if (verifiedSpan != null) {
 | 
				
			||||||
 | 
					                    sb.append("  ");
 | 
				
			||||||
 | 
					                    sb.setSpan(verifiedSpan, sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "bind: ", e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        binding.username.setText(sb);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					package awais.instagrabber.dialogs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Dialog;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.NonNull;
 | 
				
			||||||
 | 
					import androidx.annotation.Nullable;
 | 
				
			||||||
 | 
					import androidx.annotation.StringRes;
 | 
				
			||||||
 | 
					import androidx.fragment.app.DialogFragment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import awais.instagrabber.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ConfirmDialogFragment extends DialogFragment {
 | 
				
			||||||
 | 
					    private Context context;
 | 
				
			||||||
 | 
					    private ConfirmDialogFragmentCallback callback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    public static ConfirmDialogFragment newInstance(final int requestCode,
 | 
				
			||||||
 | 
					                                                    @StringRes final int title,
 | 
				
			||||||
 | 
					                                                    @StringRes final int message,
 | 
				
			||||||
 | 
					                                                    @StringRes final int positiveText,
 | 
				
			||||||
 | 
					                                                    @StringRes final int negativeText,
 | 
				
			||||||
 | 
					                                                    @StringRes final int neutralText) {
 | 
				
			||||||
 | 
					        Bundle args = new Bundle();
 | 
				
			||||||
 | 
					        args.putInt("requestCode", requestCode);
 | 
				
			||||||
 | 
					        args.putInt("title", title);
 | 
				
			||||||
 | 
					        args.putInt("message", message);
 | 
				
			||||||
 | 
					        args.putInt("positive", positiveText);
 | 
				
			||||||
 | 
					        args.putInt("negative", negativeText);
 | 
				
			||||||
 | 
					        args.putInt("neutral", neutralText);
 | 
				
			||||||
 | 
					        ConfirmDialogFragment fragment = new ConfirmDialogFragment();
 | 
				
			||||||
 | 
					        fragment.setArguments(args);
 | 
				
			||||||
 | 
					        return fragment;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ConfirmDialogFragment() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onAttach(@NonNull final Context context) {
 | 
				
			||||||
 | 
					        super.onAttach(context);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            callback = (ConfirmDialogFragmentCallback) getParentFragment();
 | 
				
			||||||
 | 
					        } catch (ClassCastException e) {
 | 
				
			||||||
 | 
					            throw new ClassCastException("Calling fragment must implement ConfirmDialogFragmentCallback interface");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.context = context;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        final Bundle arguments = getArguments();
 | 
				
			||||||
 | 
					        int title = -1;
 | 
				
			||||||
 | 
					        int message = -1;
 | 
				
			||||||
 | 
					        int positiveButtonText = R.string.ok;
 | 
				
			||||||
 | 
					        int negativeButtonText = R.string.cancel;
 | 
				
			||||||
 | 
					        int neutralButtonText = -1;
 | 
				
			||||||
 | 
					        final int requestCode;
 | 
				
			||||||
 | 
					        if (arguments != null) {
 | 
				
			||||||
 | 
					            title = arguments.getInt("title", -1);
 | 
				
			||||||
 | 
					            message = arguments.getInt("message", -1);
 | 
				
			||||||
 | 
					            positiveButtonText = arguments.getInt("positive", R.string.ok);
 | 
				
			||||||
 | 
					            negativeButtonText = arguments.getInt("negative", R.string.cancel);
 | 
				
			||||||
 | 
					            neutralButtonText = arguments.getInt("neutral", -1);
 | 
				
			||||||
 | 
					            requestCode = arguments.getInt("requestCode", 0);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            requestCode = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
 | 
				
			||||||
 | 
					                .setPositiveButton(positiveButtonText, (d, w) -> {
 | 
				
			||||||
 | 
					                    if (callback == null) return;
 | 
				
			||||||
 | 
					                    callback.onPositiveButtonClicked(requestCode);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .setNegativeButton(negativeButtonText, (dialog, which) -> {
 | 
				
			||||||
 | 
					                    if (callback == null) return;
 | 
				
			||||||
 | 
					                    callback.onNegativeButtonClicked(requestCode);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					        if (title > 0) {
 | 
				
			||||||
 | 
					            builder.setTitle(title);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (message > 0) {
 | 
				
			||||||
 | 
					            builder.setMessage(message);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (neutralButtonText > 0) {
 | 
				
			||||||
 | 
					            builder.setNeutralButton(neutralButtonText, (dialog, which) -> {
 | 
				
			||||||
 | 
					                if (callback == null) return;
 | 
				
			||||||
 | 
					                callback.onNeutralButtonClicked(requestCode);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return builder.create();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public interface ConfirmDialogFragmentCallback {
 | 
				
			||||||
 | 
					        void onPositiveButtonClicked(int requestCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void onNegativeButtonClicked(int requestCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void onNeutralButtonClicked(int requestCode);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -34,11 +34,17 @@ import java.util.Optional;
 | 
				
			|||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import awais.instagrabber.ProfileNavGraphDirections;
 | 
				
			||||||
import awais.instagrabber.R;
 | 
					import awais.instagrabber.R;
 | 
				
			||||||
import awais.instagrabber.UserSearchNavGraphDirections;
 | 
					import awais.instagrabber.UserSearchNavGraphDirections;
 | 
				
			||||||
 | 
					import awais.instagrabber.adapters.DirectPendingUsersAdapter;
 | 
				
			||||||
 | 
					import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;
 | 
				
			||||||
 | 
					import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;
 | 
				
			||||||
import awais.instagrabber.adapters.DirectUsersAdapter;
 | 
					import awais.instagrabber.adapters.DirectUsersAdapter;
 | 
				
			||||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
 | 
					import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
 | 
				
			||||||
import awais.instagrabber.databinding.FragmentDirectMessagesSettingsBinding;
 | 
					import awais.instagrabber.databinding.FragmentDirectMessagesSettingsBinding;
 | 
				
			||||||
 | 
					import awais.instagrabber.dialogs.ConfirmDialogFragment;
 | 
				
			||||||
 | 
					import awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback;
 | 
				
			||||||
import awais.instagrabber.dialogs.MultiOptionDialogFragment;
 | 
					import awais.instagrabber.dialogs.MultiOptionDialogFragment;
 | 
				
			||||||
import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option;
 | 
					import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option;
 | 
				
			||||||
import awais.instagrabber.fragments.UserSearchFragment;
 | 
					import awais.instagrabber.fragments.UserSearchFragment;
 | 
				
			||||||
@ -46,16 +52,21 @@ import awais.instagrabber.fragments.UserSearchFragmentDirections;
 | 
				
			|||||||
import awais.instagrabber.models.Resource;
 | 
					import awais.instagrabber.models.Resource;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.User;
 | 
					import awais.instagrabber.repositories.responses.User;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThread;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
 | 
					import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
 | 
				
			||||||
import awais.instagrabber.viewmodels.DirectInboxViewModel;
 | 
					import awais.instagrabber.viewmodels.DirectInboxViewModel;
 | 
				
			||||||
import awais.instagrabber.viewmodels.DirectSettingsViewModel;
 | 
					import awais.instagrabber.viewmodels.DirectSettingsViewModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class DirectMessageSettingsFragment extends Fragment {
 | 
					public class DirectMessageSettingsFragment extends Fragment implements ConfirmDialogFragmentCallback {
 | 
				
			||||||
    private static final String TAG = DirectMessageSettingsFragment.class.getSimpleName();
 | 
					    private static final String TAG = DirectMessageSettingsFragment.class.getSimpleName();
 | 
				
			||||||
 | 
					    public static final int APPROVAL_REQUIRED_REQUEST_CODE = 200;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private FragmentDirectMessagesSettingsBinding binding;
 | 
					    private FragmentDirectMessagesSettingsBinding binding;
 | 
				
			||||||
    private DirectSettingsViewModel viewModel;
 | 
					    private DirectSettingsViewModel viewModel;
 | 
				
			||||||
    private DirectUsersAdapter usersAdapter;
 | 
					    private DirectUsersAdapter usersAdapter;
 | 
				
			||||||
 | 
					    private boolean isPendingRequestsSetupDone = false;
 | 
				
			||||||
 | 
					    private DirectPendingUsersAdapter pendingUsersAdapter;
 | 
				
			||||||
 | 
					    private Set<User> approvalRequiredUsers;
 | 
				
			||||||
    // private List<Option<String>> options;
 | 
					    // private List<Option<String>> options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -165,6 +176,7 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
    public void onDestroyView() {
 | 
					    public void onDestroyView() {
 | 
				
			||||||
        super.onDestroyView();
 | 
					        super.onDestroyView();
 | 
				
			||||||
        binding = null;
 | 
					        binding = null;
 | 
				
			||||||
 | 
					        isPendingRequestsSetupDone = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setupObservers() {
 | 
					    private void setupObservers() {
 | 
				
			||||||
@ -178,18 +190,21 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
            usersAdapter.setAdminUserIds(adminUserIds);
 | 
					            usersAdapter.setAdminUserIds(adminUserIds);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        viewModel.getMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
 | 
					        viewModel.getMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
 | 
				
			||||||
 | 
					        if (viewModel.isViewerAdmin()) {
 | 
				
			||||||
 | 
					            viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required));
 | 
				
			||||||
 | 
					            viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        final NavController navController = NavHostFragment.findNavController(this);
 | 
					        final NavController navController = NavHostFragment.findNavController(this);
 | 
				
			||||||
        final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
 | 
					        final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
 | 
				
			||||||
        if (backStackEntry != null) {
 | 
					        if (backStackEntry != null) {
 | 
				
			||||||
            final MutableLiveData<Object> resultLiveData = backStackEntry.getSavedStateHandle().getLiveData("result");
 | 
					            final MutableLiveData<Object> resultLiveData = backStackEntry.getSavedStateHandle().getLiveData("result");
 | 
				
			||||||
            resultLiveData.observe(getViewLifecycleOwner(), result -> {
 | 
					            resultLiveData.observe(getViewLifecycleOwner(), result -> {
 | 
				
			||||||
                LiveData<Resource<Object>> detailsChangeResourceLiveData = null;
 | 
					 | 
				
			||||||
                if ((result instanceof RankedRecipient)) {
 | 
					                if ((result instanceof RankedRecipient)) {
 | 
				
			||||||
                    final RankedRecipient recipient = (RankedRecipient) result;
 | 
					                    final RankedRecipient recipient = (RankedRecipient) result;
 | 
				
			||||||
                    final User user = getUser(recipient);
 | 
					                    final User user = getUser(recipient);
 | 
				
			||||||
                    // Log.d(TAG, "result: " + user);
 | 
					                    // Log.d(TAG, "result: " + user);
 | 
				
			||||||
                    if (user != null) {
 | 
					                    if (user != null) {
 | 
				
			||||||
                        detailsChangeResourceLiveData = viewModel.addMembers(Collections.singleton(recipient.getUser()));
 | 
					                        addMembers(Collections.singleton(recipient.getUser()));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } else if ((result instanceof Set)) {
 | 
					                } else if ((result instanceof Set)) {
 | 
				
			||||||
                    try {
 | 
					                    try {
 | 
				
			||||||
@ -201,19 +216,35 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
                                                          .filter(Objects::nonNull)
 | 
					                                                          .filter(Objects::nonNull)
 | 
				
			||||||
                                                          .collect(Collectors.toSet());
 | 
					                                                          .collect(Collectors.toSet());
 | 
				
			||||||
                        // Log.d(TAG, "result: " + users);
 | 
					                        // Log.d(TAG, "result: " + users);
 | 
				
			||||||
                        detailsChangeResourceLiveData = viewModel.addMembers(users);
 | 
					                        addMembers(users);
 | 
				
			||||||
                    } catch (Exception e) {
 | 
					                    } catch (Exception e) {
 | 
				
			||||||
                        Log.e(TAG, "search users result: ", e);
 | 
					                        Log.e(TAG, "search users result: ", e);
 | 
				
			||||||
                        Snackbar.make(binding.getRoot(), e.getMessage() != null ? e.getMessage() : "", Snackbar.LENGTH_LONG).show();
 | 
					                        Snackbar.make(binding.getRoot(), e.getMessage() != null ? e.getMessage() : "", Snackbar.LENGTH_LONG).show();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (detailsChangeResourceLiveData != null) {
 | 
					 | 
				
			||||||
                    observeDetailsChange(detailsChangeResourceLiveData);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void addMembers(final Set<User> users) {
 | 
				
			||||||
 | 
					        final Boolean approvalRequired = viewModel.getApprovalRequiredToJoin().getValue();
 | 
				
			||||||
 | 
					        if (!viewModel.isViewerAdmin() && approvalRequired != null && approvalRequired) {
 | 
				
			||||||
 | 
					            approvalRequiredUsers = users;
 | 
				
			||||||
 | 
					            final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance(
 | 
				
			||||||
 | 
					                    APPROVAL_REQUIRED_REQUEST_CODE,
 | 
				
			||||||
 | 
					                    R.string.admin_approval_required,
 | 
				
			||||||
 | 
					                    R.string.admin_approval_required_description,
 | 
				
			||||||
 | 
					                    R.string.ok,
 | 
				
			||||||
 | 
					                    R.string.cancel,
 | 
				
			||||||
 | 
					                    -1
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        final LiveData<Resource<Object>> detailsChangeResourceLiveData = viewModel.addMembers(users);
 | 
				
			||||||
 | 
					        observeDetailsChange(detailsChangeResourceLiveData);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    private User getUser(@NonNull final RankedRecipient recipient) {
 | 
					    private User getUser(@NonNull final RankedRecipient recipient) {
 | 
				
			||||||
        User user = null;
 | 
					        User user = null;
 | 
				
			||||||
@ -279,16 +310,28 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
        binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle());
 | 
					        binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle());
 | 
				
			||||||
        binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> {
 | 
					        binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> {
 | 
				
			||||||
            final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.mute() : viewModel.unmute();
 | 
					            final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.mute() : viewModel.unmute();
 | 
				
			||||||
            handleMuteChangeResource(resourceLiveData, buttonView);
 | 
					            handleSwitchChangeResource(resourceLiveData, buttonView);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        binding.muteMentionsLabel.setOnClickListener(v -> binding.muteMentions.toggle());
 | 
					        binding.muteMentionsLabel.setOnClickListener(v -> binding.muteMentions.toggle());
 | 
				
			||||||
        binding.muteMentions.setOnCheckedChangeListener((buttonView, isChecked) -> {
 | 
					        binding.muteMentions.setOnCheckedChangeListener((buttonView, isChecked) -> {
 | 
				
			||||||
            final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.muteMentions() : viewModel.unmuteMentions();
 | 
					            final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.muteMentions() : viewModel.unmuteMentions();
 | 
				
			||||||
            handleMuteChangeResource(resourceLiveData, buttonView);
 | 
					            handleSwitchChangeResource(resourceLiveData, buttonView);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        if (!viewModel.isViewerAdmin()) {
 | 
				
			||||||
 | 
					            binding.pendingMembersGroup.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            binding.approvalRequired.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            binding.approvalRequiredLabel.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        binding.approvalRequired.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					        binding.approvalRequiredLabel.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					        binding.approvalRequiredLabel.setOnClickListener(v -> binding.approvalRequired.toggle());
 | 
				
			||||||
 | 
					        binding.approvalRequired.setOnCheckedChangeListener((buttonView, isChecked) -> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void handleMuteChangeResource(final LiveData<Resource<Object>> resourceLiveData, final CompoundButton buttonView) {
 | 
					    private void handleSwitchChangeResource(final LiveData<Resource<Object>> resourceLiveData, final CompoundButton buttonView) {
 | 
				
			||||||
        resourceLiveData.observe(getViewLifecycleOwner(), resource -> {
 | 
					        resourceLiveData.observe(getViewLifecycleOwner(), resource -> {
 | 
				
			||||||
            if (resource == null) return;
 | 
					            if (resource == null) return;
 | 
				
			||||||
            switch (resource.status) {
 | 
					            switch (resource.status) {
 | 
				
			||||||
@ -297,6 +340,7 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case ERROR:
 | 
					                case ERROR:
 | 
				
			||||||
                    buttonView.setEnabled(true);
 | 
					                    buttonView.setEnabled(true);
 | 
				
			||||||
 | 
					                    buttonView.setChecked(!buttonView.isChecked());
 | 
				
			||||||
                    if (resource.message != null) {
 | 
					                    if (resource.message != null) {
 | 
				
			||||||
                        Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
 | 
					                        Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -316,7 +360,10 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
        usersAdapter = new DirectUsersAdapter(
 | 
					        usersAdapter = new DirectUsersAdapter(
 | 
				
			||||||
                inviter != null ? inviter.getPk() : -1,
 | 
					                inviter != null ? inviter.getPk() : -1,
 | 
				
			||||||
                (position, user, selected) -> {
 | 
					                (position, user, selected) -> {
 | 
				
			||||||
                    // navigate to profile
 | 
					                    final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections
 | 
				
			||||||
 | 
					                            .actionGlobalProfileFragment()
 | 
				
			||||||
 | 
					                            .setUsername("@" + user.getUsername());
 | 
				
			||||||
 | 
					                    NavHostFragment.findNavController(this).navigate(directions);
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                (position, user) -> {
 | 
					                (position, user) -> {
 | 
				
			||||||
                    final ArrayList<Option<String>> options = viewModel.createUserOptions(user);
 | 
					                    final ArrayList<Option<String>> options = viewModel.createUserOptions(user);
 | 
				
			||||||
@ -340,6 +387,45 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
        binding.users.setAdapter(usersAdapter);
 | 
					        binding.users.setAdapter(usersAdapter);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void setPendingRequests(final DirectThreadParticipantRequestsResponse requests) {
 | 
				
			||||||
 | 
					        if (requests == null || requests.getUsers() == null || requests.getUsers().isEmpty()) {
 | 
				
			||||||
 | 
					            binding.pendingMembersGroup.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!isPendingRequestsSetupDone) {
 | 
				
			||||||
 | 
					            final Context context = getContext();
 | 
				
			||||||
 | 
					            if (context == null) return;
 | 
				
			||||||
 | 
					            binding.pendingMembers.setLayoutManager(new LinearLayoutManager(context));
 | 
				
			||||||
 | 
					            pendingUsersAdapter = new DirectPendingUsersAdapter(new PendingUserCallback() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onClick(final int position, final PendingUser pendingUser) {
 | 
				
			||||||
 | 
					                    final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections
 | 
				
			||||||
 | 
					                            .actionGlobalProfileFragment()
 | 
				
			||||||
 | 
					                            .setUsername("@" + pendingUser.getUser().getUsername());
 | 
				
			||||||
 | 
					                    NavHostFragment.findNavController(DirectMessageSettingsFragment.this).navigate(directions);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onApprove(final int position, final PendingUser pendingUser) {
 | 
				
			||||||
 | 
					                    final LiveData<Resource<Object>> resourceLiveData = viewModel.approveUsers(Collections.singletonList(pendingUser.getUser()));
 | 
				
			||||||
 | 
					                    observeApprovalChange(resourceLiveData, position, pendingUser);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onDeny(final int position, final PendingUser pendingUser) {
 | 
				
			||||||
 | 
					                    final LiveData<Resource<Object>> resourceLiveData = viewModel.denyUsers(Collections.singletonList(pendingUser.getUser()));
 | 
				
			||||||
 | 
					                    observeApprovalChange(resourceLiveData, position, pendingUser);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            binding.pendingMembers.setAdapter(pendingUsersAdapter);
 | 
				
			||||||
 | 
					            binding.pendingMembersGroup.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					            isPendingRequestsSetupDone = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (pendingUsersAdapter != null) {
 | 
				
			||||||
 | 
					            pendingUsersAdapter.submitPendingRequests(requests);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void observeDetailsChange(@NonNull final LiveData<Resource<Object>> detailsChangeResourceLiveData) {
 | 
					    private void observeDetailsChange(@NonNull final LiveData<Resource<Object>> detailsChangeResourceLiveData) {
 | 
				
			||||||
        detailsChangeResourceLiveData.observe(getViewLifecycleOwner(), resource -> {
 | 
					        detailsChangeResourceLiveData.observe(getViewLifecycleOwner(), resource -> {
 | 
				
			||||||
            if (resource == null) return;
 | 
					            if (resource == null) return;
 | 
				
			||||||
@ -356,6 +442,48 @@ public class DirectMessageSettingsFragment extends Fragment {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void observeApprovalChange(@NonNull final LiveData<Resource<Object>> detailsChangeResourceLiveData,
 | 
				
			||||||
 | 
					                                       final int position,
 | 
				
			||||||
 | 
					                                       @NonNull final PendingUser pendingUser) {
 | 
				
			||||||
 | 
					        detailsChangeResourceLiveData.observe(getViewLifecycleOwner(), resource -> {
 | 
				
			||||||
 | 
					            if (resource == null) return;
 | 
				
			||||||
 | 
					            switch (resource.status) {
 | 
				
			||||||
 | 
					                case SUCCESS:
 | 
				
			||||||
 | 
					                    // pending user will be removed from the list, so no need to set the progress to false
 | 
				
			||||||
 | 
					                    // pendingUser.setInProgress(false);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case LOADING:
 | 
				
			||||||
 | 
					                    pendingUser.setInProgress(true);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case ERROR:
 | 
				
			||||||
 | 
					                    pendingUser.setInProgress(false);
 | 
				
			||||||
 | 
					                    if (resource.message != null) {
 | 
				
			||||||
 | 
					                        Snackbar.make(binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            pendingUsersAdapter.notifyItemChanged(position);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onPositiveButtonClicked(final int requestCode) {
 | 
				
			||||||
 | 
					        if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE && approvalRequiredUsers != null) {
 | 
				
			||||||
 | 
					            final LiveData<Resource<Object>> detailsChangeResourceLiveData = viewModel.addMembers(approvalRequiredUsers);
 | 
				
			||||||
 | 
					            observeDetailsChange(detailsChangeResourceLiveData);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onNegativeButtonClicked(final int requestCode) {
 | 
				
			||||||
 | 
					        if (requestCode == APPROVAL_REQUIRED_REQUEST_CODE) {
 | 
				
			||||||
 | 
					            approvalRequiredUsers = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onNeutralButtonClicked(final int requestCode) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // class ChangeSettings extends AsyncTask<String, Void, Void> {
 | 
					    // class ChangeSettings extends AsyncTask<String, Void, Void> {
 | 
				
			||||||
    //     String action, argument;
 | 
					    //     String action, argument;
 | 
				
			||||||
    //     boolean ok = false;
 | 
					    //     boolean ok = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import android.animation.Animator;
 | 
				
			|||||||
import android.animation.AnimatorListenerAdapter;
 | 
					import android.animation.AnimatorListenerAdapter;
 | 
				
			||||||
import android.animation.AnimatorSet;
 | 
					import android.animation.AnimatorSet;
 | 
				
			||||||
import android.animation.ObjectAnimator;
 | 
					import android.animation.ObjectAnimator;
 | 
				
			||||||
 | 
					import android.annotation.SuppressLint;
 | 
				
			||||||
import android.app.Activity;
 | 
					import android.app.Activity;
 | 
				
			||||||
import android.content.Context;
 | 
					import android.content.Context;
 | 
				
			||||||
import android.content.Intent;
 | 
					import android.content.Intent;
 | 
				
			||||||
@ -49,6 +50,8 @@ import androidx.transition.TransitionManager;
 | 
				
			|||||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
 | 
					import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
 | 
				
			||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
 | 
					import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.android.material.badge.BadgeDrawable;
 | 
				
			||||||
 | 
					import com.google.android.material.badge.BadgeUtils;
 | 
				
			||||||
import com.google.android.material.snackbar.Snackbar;
 | 
					import com.google.android.material.snackbar.Snackbar;
 | 
				
			||||||
import com.google.common.collect.ImmutableList;
 | 
					import com.google.common.collect.ImmutableList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -58,6 +61,7 @@ import java.util.List;
 | 
				
			|||||||
import java.util.Optional;
 | 
					import java.util.Optional;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import awais.instagrabber.ProfileNavGraphDirections;
 | 
				
			||||||
import awais.instagrabber.R;
 | 
					import awais.instagrabber.R;
 | 
				
			||||||
import awais.instagrabber.UserSearchNavGraphDirections;
 | 
					import awais.instagrabber.UserSearchNavGraphDirections;
 | 
				
			||||||
import awais.instagrabber.activities.CameraActivity;
 | 
					import awais.instagrabber.activities.CameraActivity;
 | 
				
			||||||
@ -140,6 +144,8 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
 | 
				
			|||||||
    private DirectItem itemToForward;
 | 
					    private DirectItem itemToForward;
 | 
				
			||||||
    private MutableLiveData<Object> backStackSavedStateResultLiveData;
 | 
					    private MutableLiveData<Object> backStackSavedStateResultLiveData;
 | 
				
			||||||
    private int prevLength;
 | 
					    private int prevLength;
 | 
				
			||||||
 | 
					    private BadgeDrawable pendingRequestCountBadgeDrawable;
 | 
				
			||||||
 | 
					    private boolean isPendingRequestCountBadgeAttached = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final AppExecutors appExecutors = AppExecutors.getInstance();
 | 
					    private final AppExecutors appExecutors = AppExecutors.getInstance();
 | 
				
			||||||
    private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
 | 
					    private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
 | 
				
			||||||
@ -413,6 +419,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        binding.send.stopScale();
 | 
					        binding.send.stopScale();
 | 
				
			||||||
        setupBackStackResultObserver();
 | 
					        setupBackStackResultObserver();
 | 
				
			||||||
 | 
					        attachPendingRequestsBadge(viewModel.getPendingRequestsCount().getValue());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -421,6 +428,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
 | 
				
			|||||||
        cleanup();
 | 
					        cleanup();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint("UnsafeExperimentalUsageError")
 | 
				
			||||||
    private void cleanup() {
 | 
					    private void cleanup() {
 | 
				
			||||||
        if (prevTitleRunnable != null) {
 | 
					        if (prevTitleRunnable != null) {
 | 
				
			||||||
            appExecutors.mainThread().cancel(prevTitleRunnable);
 | 
					            appExecutors.mainThread().cancel(prevTitleRunnable);
 | 
				
			||||||
@ -439,6 +447,11 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
 | 
				
			|||||||
                ((DirectItemViewHolder) holder).cleanup();
 | 
					                ((DirectItemViewHolder) holder).cleanup();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        isPendingRequestCountBadgeAttached = false;
 | 
				
			||||||
 | 
					        if (pendingRequestCountBadgeDrawable != null) {
 | 
				
			||||||
 | 
					            BadgeUtils.detachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);
 | 
				
			||||||
 | 
					            pendingRequestCountBadgeDrawable = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void init() {
 | 
					    private void init() {
 | 
				
			||||||
@ -705,6 +718,28 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            prevLength = length;
 | 
					            prevLength = length;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        viewModel.getPendingRequestsCount().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint("UnsafeExperimentalUsageError")
 | 
				
			||||||
 | 
					    private void attachPendingRequestsBadge(@Nullable final Integer count) {
 | 
				
			||||||
 | 
					        if (pendingRequestCountBadgeDrawable == null) {
 | 
				
			||||||
 | 
					            final Context context = getContext();
 | 
				
			||||||
 | 
					            if (context == null) return;
 | 
				
			||||||
 | 
					            pendingRequestCountBadgeDrawable = BadgeDrawable.create(context);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count == null || count == 0) {
 | 
				
			||||||
 | 
					            BadgeUtils.detachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);
 | 
				
			||||||
 | 
					            isPendingRequestCountBadgeAttached = false;
 | 
				
			||||||
 | 
					            pendingRequestCountBadgeDrawable.setNumber(0);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (pendingRequestCountBadgeDrawable.getNumber() == count) return;
 | 
				
			||||||
 | 
					        pendingRequestCountBadgeDrawable.setNumber(count);
 | 
				
			||||||
 | 
					        if (!isPendingRequestCountBadgeAttached) {
 | 
				
			||||||
 | 
					            BadgeUtils.attachBadgeDrawable(pendingRequestCountBadgeDrawable, fragmentActivity.getToolbar(), R.id.info);
 | 
				
			||||||
 | 
					            isPendingRequestCountBadgeAttached = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void showExtraInputOption(final boolean show) {
 | 
					    private void showExtraInputOption(final boolean show) {
 | 
				
			||||||
@ -1396,9 +1431,10 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void navigateToUser(@NonNull final String username) {
 | 
					    private void navigateToUser(@NonNull final String username) {
 | 
				
			||||||
        final Bundle bundle = new Bundle();
 | 
					        final ProfileNavGraphDirections.ActionGlobalProfileFragment direction = ProfileNavGraphDirections
 | 
				
			||||||
        bundle.putString("username", "@" + username);
 | 
					                .actionGlobalProfileFragment()
 | 
				
			||||||
        NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(R.id.action_global_profileFragment, bundle);
 | 
					                .setUsername("@" + username);
 | 
				
			||||||
 | 
					        NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(direction);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static class ItemsAdapterDataMerger extends MediatorLiveData<Pair<User, DirectThread>> {
 | 
					    public static class ItemsAdapterDataMerger extends MediatorLiveData<Pair<User, DirectThread>> {
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThread;
 | 
				
			|||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
 | 
				
			||||||
import retrofit2.Call;
 | 
					import retrofit2.Call;
 | 
				
			||||||
import retrofit2.http.FieldMap;
 | 
					import retrofit2.http.FieldMap;
 | 
				
			||||||
@ -15,6 +16,7 @@ import retrofit2.http.FormUrlEncoded;
 | 
				
			|||||||
import retrofit2.http.GET;
 | 
					import retrofit2.http.GET;
 | 
				
			||||||
import retrofit2.http.POST;
 | 
					import retrofit2.http.POST;
 | 
				
			||||||
import retrofit2.http.Path;
 | 
					import retrofit2.http.Path;
 | 
				
			||||||
 | 
					import retrofit2.http.Query;
 | 
				
			||||||
import retrofit2.http.QueryMap;
 | 
					import retrofit2.http.QueryMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public interface DirectMessagesRepository {
 | 
					public interface DirectMessagesRepository {
 | 
				
			||||||
@ -95,4 +97,19 @@ public interface DirectMessagesRepository {
 | 
				
			|||||||
    @POST("/api/v1/direct_v2/threads/{threadId}/unmute_mentions/")
 | 
					    @POST("/api/v1/direct_v2/threads/{threadId}/unmute_mentions/")
 | 
				
			||||||
    Call<String> unmuteMentions(@Path("threadId") String threadId,
 | 
					    Call<String> unmuteMentions(@Path("threadId") String threadId,
 | 
				
			||||||
                                @FieldMap final Map<String, String> form);
 | 
					                                @FieldMap final Map<String, String> form);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @GET("/api/v1/direct_v2/threads/{threadId}/participant_requests/")
 | 
				
			||||||
 | 
					    Call<DirectThreadParticipantRequestsResponse> participantRequests(@Path("threadId") String threadId,
 | 
				
			||||||
 | 
					                                                                      @Query("page_size") int pageSize,
 | 
				
			||||||
 | 
					                                                                      @Query("cursor") String cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @FormUrlEncoded
 | 
				
			||||||
 | 
					    @POST("/api/v1/direct_v2/threads/{threadId}/approve_participant_requests/")
 | 
				
			||||||
 | 
					    Call<DirectThreadDetailsChangeResponse> approveParticipantRequests(@Path("threadId") String threadId,
 | 
				
			||||||
 | 
					                                                                       @FieldMap final Map<String, String> form);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @FormUrlEncoded
 | 
				
			||||||
 | 
					    @POST("/api/v1/direct_v2/threads/{threadId}/deny_participant_requests/")
 | 
				
			||||||
 | 
					    Call<DirectThreadDetailsChangeResponse> declineParticipantRequests(@Path("threadId") String threadId,
 | 
				
			||||||
 | 
					                                                                       @FieldMap final Map<String, String> form);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -41,6 +41,7 @@ public class DirectThread implements Serializable {
 | 
				
			|||||||
    private final boolean isSpam;
 | 
					    private final boolean isSpam;
 | 
				
			||||||
    private final DirectItem lastPermanentItem;
 | 
					    private final DirectItem lastPermanentItem;
 | 
				
			||||||
    private final DirectThreadDirectStory directStory;
 | 
					    private final DirectThreadDirectStory directStory;
 | 
				
			||||||
 | 
					    private final boolean approvalRequiredForNewMembers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public DirectThread(final String threadId,
 | 
					    public DirectThread(final String threadId,
 | 
				
			||||||
                        final String threadV2Id,
 | 
					                        final String threadV2Id,
 | 
				
			||||||
@ -72,7 +73,8 @@ public class DirectThread implements Serializable {
 | 
				
			|||||||
                        final String oldestCursor,
 | 
					                        final String oldestCursor,
 | 
				
			||||||
                        final boolean isSpam,
 | 
					                        final boolean isSpam,
 | 
				
			||||||
                        final DirectItem lastPermanentItem,
 | 
					                        final DirectItem lastPermanentItem,
 | 
				
			||||||
                        final DirectThreadDirectStory directStory) {
 | 
					                        final DirectThreadDirectStory directStory,
 | 
				
			||||||
 | 
					                        final boolean approvalRequiredForNewMembers) {
 | 
				
			||||||
        this.threadId = threadId;
 | 
					        this.threadId = threadId;
 | 
				
			||||||
        this.threadV2Id = threadV2Id;
 | 
					        this.threadV2Id = threadV2Id;
 | 
				
			||||||
        this.users = users;
 | 
					        this.users = users;
 | 
				
			||||||
@ -104,6 +106,7 @@ public class DirectThread implements Serializable {
 | 
				
			|||||||
        this.isSpam = isSpam;
 | 
					        this.isSpam = isSpam;
 | 
				
			||||||
        this.lastPermanentItem = lastPermanentItem;
 | 
					        this.lastPermanentItem = lastPermanentItem;
 | 
				
			||||||
        this.directStory = directStory;
 | 
					        this.directStory = directStory;
 | 
				
			||||||
 | 
					        this.approvalRequiredForNewMembers = approvalRequiredForNewMembers;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String getThreadId() {
 | 
					    public String getThreadId() {
 | 
				
			||||||
@ -238,6 +241,10 @@ public class DirectThread implements Serializable {
 | 
				
			|||||||
        return directStory;
 | 
					        return directStory;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isApprovalRequiredForNewMembers() {
 | 
				
			||||||
 | 
					        return approvalRequiredForNewMembers;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Nullable
 | 
					    @Nullable
 | 
				
			||||||
    public DirectItem getFirstDirectItem() {
 | 
					    public DirectItem getFirstDirectItem() {
 | 
				
			||||||
        DirectItem firstItem = null;
 | 
					        DirectItem firstItem = null;
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					package awais.instagrabber.repositories.responses.directmessages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DirectThreadParticipantRequestsResponse implements Serializable, Cloneable {
 | 
				
			||||||
 | 
					    private List<User> users;
 | 
				
			||||||
 | 
					    private final Map<Long, String> requesterUsernames;
 | 
				
			||||||
 | 
					    private final String cursor;
 | 
				
			||||||
 | 
					    private final int totalThreadParticipants;
 | 
				
			||||||
 | 
					    private final int totalParticipantRequests;
 | 
				
			||||||
 | 
					    private final String status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DirectThreadParticipantRequestsResponse(final List<User> users,
 | 
				
			||||||
 | 
					                                                   final Map<Long, String> requesterUsernames,
 | 
				
			||||||
 | 
					                                                   final String cursor,
 | 
				
			||||||
 | 
					                                                   final int totalThreadParticipants,
 | 
				
			||||||
 | 
					                                                   final int totalParticipantRequests,
 | 
				
			||||||
 | 
					                                                   final String status) {
 | 
				
			||||||
 | 
					        this.users = users;
 | 
				
			||||||
 | 
					        this.requesterUsernames = requesterUsernames;
 | 
				
			||||||
 | 
					        this.cursor = cursor;
 | 
				
			||||||
 | 
					        this.totalThreadParticipants = totalThreadParticipants;
 | 
				
			||||||
 | 
					        this.totalParticipantRequests = totalParticipantRequests;
 | 
				
			||||||
 | 
					        this.status = status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public List<User> getUsers() {
 | 
				
			||||||
 | 
					        return users;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setUsers(final List<User> users) {
 | 
				
			||||||
 | 
					        this.users = users;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Map<Long, String> getRequesterUsernames() {
 | 
				
			||||||
 | 
					        return requesterUsernames;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getCursor() {
 | 
				
			||||||
 | 
					        return cursor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getTotalThreadParticipants() {
 | 
				
			||||||
 | 
					        return totalThreadParticipants;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getTotalParticipantRequests() {
 | 
				
			||||||
 | 
					        return totalParticipantRequests;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getStatus() {
 | 
				
			||||||
 | 
					        return status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Object clone() throws CloneNotSupportedException {
 | 
				
			||||||
 | 
					        return super.clone();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -5,6 +5,7 @@ import android.content.res.Resources;
 | 
				
			|||||||
import android.util.Log;
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.annotation.NonNull;
 | 
					import androidx.annotation.NonNull;
 | 
				
			||||||
 | 
					import androidx.annotation.Nullable;
 | 
				
			||||||
import androidx.annotation.StringRes;
 | 
					import androidx.annotation.StringRes;
 | 
				
			||||||
import androidx.core.util.Pair;
 | 
					import androidx.core.util.Pair;
 | 
				
			||||||
import androidx.lifecycle.AndroidViewModel;
 | 
					import androidx.lifecycle.AndroidViewModel;
 | 
				
			||||||
@ -20,6 +21,7 @@ import java.io.IOException;
 | 
				
			|||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Locale;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,6 +33,7 @@ import awais.instagrabber.repositories.responses.FriendshipRestrictResponse;
 | 
				
			|||||||
import awais.instagrabber.repositories.responses.User;
 | 
					import awais.instagrabber.repositories.responses.User;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThread;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
 | 
				
			||||||
import awais.instagrabber.utils.Constants;
 | 
					import awais.instagrabber.utils.Constants;
 | 
				
			||||||
import awais.instagrabber.utils.CookieUtils;
 | 
					import awais.instagrabber.utils.CookieUtils;
 | 
				
			||||||
import awais.instagrabber.utils.TextUtils;
 | 
					import awais.instagrabber.utils.TextUtils;
 | 
				
			||||||
@ -61,6 +64,8 @@ public class DirectSettingsViewModel extends AndroidViewModel {
 | 
				
			|||||||
    private final MutableLiveData<List<Long>> adminUserIds = new MutableLiveData<>(Collections.emptyList());
 | 
					    private final MutableLiveData<List<Long>> adminUserIds = new MutableLiveData<>(Collections.emptyList());
 | 
				
			||||||
    private final MutableLiveData<Boolean> muted = new MutableLiveData<>(false);
 | 
					    private final MutableLiveData<Boolean> muted = new MutableLiveData<>(false);
 | 
				
			||||||
    private final MutableLiveData<Boolean> mentionsMuted = new MutableLiveData<>(false);
 | 
					    private final MutableLiveData<Boolean> mentionsMuted = new MutableLiveData<>(false);
 | 
				
			||||||
 | 
					    private final MutableLiveData<Boolean> approvalRequiredToJoin = new MutableLiveData<>(false);
 | 
				
			||||||
 | 
					    private final MutableLiveData<DirectThreadParticipantRequestsResponse> pendingRequests = new MutableLiveData<>(null);
 | 
				
			||||||
    private final DirectMessagesService directMessagesService;
 | 
					    private final DirectMessagesService directMessagesService;
 | 
				
			||||||
    private final long userId;
 | 
					    private final long userId;
 | 
				
			||||||
    private final Resources resources;
 | 
					    private final Resources resources;
 | 
				
			||||||
@ -107,6 +112,10 @@ public class DirectSettingsViewModel extends AndroidViewModel {
 | 
				
			|||||||
        viewerIsAdmin = adminUserIds.contains(userId);
 | 
					        viewerIsAdmin = adminUserIds.contains(userId);
 | 
				
			||||||
        muted.postValue(thread.isMuted());
 | 
					        muted.postValue(thread.isMuted());
 | 
				
			||||||
        mentionsMuted.postValue(thread.isMentionsMuted());
 | 
					        mentionsMuted.postValue(thread.isMentionsMuted());
 | 
				
			||||||
 | 
					        approvalRequiredToJoin.postValue(thread.isApprovalRequiredForNewMembers());
 | 
				
			||||||
 | 
					        if (thread.isGroup() && viewerIsAdmin) {
 | 
				
			||||||
 | 
					            fetchPendingRequests();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean isGroup() {
 | 
					    public boolean isGroup() {
 | 
				
			||||||
@ -140,6 +149,18 @@ public class DirectSettingsViewModel extends AndroidViewModel {
 | 
				
			|||||||
        return muted;
 | 
					        return muted;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LiveData<Boolean> getApprovalRequiredToJoin() {
 | 
				
			||||||
 | 
					        return approvalRequiredToJoin;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LiveData<DirectThreadParticipantRequestsResponse> getPendingRequests() {
 | 
				
			||||||
 | 
					        return pendingRequests;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isViewerAdmin() {
 | 
				
			||||||
 | 
					        return viewerIsAdmin;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public LiveData<Resource<Object>> updateTitle(final String newTitle) {
 | 
					    public LiveData<Resource<Object>> updateTitle(final String newTitle) {
 | 
				
			||||||
        final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
 | 
					        final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
 | 
				
			||||||
        final Call<DirectThreadDetailsChangeResponse> addUsersRequest = directMessagesService
 | 
					        final Call<DirectThreadDetailsChangeResponse> addUsersRequest = directMessagesService
 | 
				
			||||||
@ -454,9 +475,52 @@ public class DirectSettingsViewModel extends AndroidViewModel {
 | 
				
			|||||||
        return data;
 | 
					        return data;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LiveData<Resource<Object>> approveUsers(final List<User> users) {
 | 
				
			||||||
 | 
					        final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
 | 
				
			||||||
 | 
					        data.postValue(Resource.loading(null));
 | 
				
			||||||
 | 
					        final Call<DirectThreadDetailsChangeResponse> approveUsersRequest = directMessagesService
 | 
				
			||||||
 | 
					                .approveParticipantRequests(thread.getThreadId(), users.stream().map(User::getPk).collect(Collectors.toList()));
 | 
				
			||||||
 | 
					        handleDetailsChangeRequest(data, approveUsersRequest);
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LiveData<Resource<Object>> denyUsers(final List<User> users) {
 | 
				
			||||||
 | 
					        final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
 | 
				
			||||||
 | 
					        data.postValue(Resource.loading(null));
 | 
				
			||||||
 | 
					        final Call<DirectThreadDetailsChangeResponse> approveUsersRequest = directMessagesService
 | 
				
			||||||
 | 
					                .declineParticipantRequests(thread.getThreadId(), users.stream().map(User::getPk).collect(Collectors.toList()));
 | 
				
			||||||
 | 
					        handleDetailsChangeRequest(data, approveUsersRequest, () -> {
 | 
				
			||||||
 | 
					            final DirectThreadParticipantRequestsResponse pendingRequestsValue = pendingRequests.getValue();
 | 
				
			||||||
 | 
					            if (pendingRequestsValue == null) return;
 | 
				
			||||||
 | 
					            final List<User> pendingUsers = pendingRequestsValue.getUsers();
 | 
				
			||||||
 | 
					            if (pendingUsers == null || pendingUsers.isEmpty()) return;
 | 
				
			||||||
 | 
					            final List<User> filtered = pendingUsers.stream()
 | 
				
			||||||
 | 
					                                                    .filter(o -> !users.contains(o))
 | 
				
			||||||
 | 
					                                                    .collect(Collectors.toList());
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                final DirectThreadParticipantRequestsResponse clone = (DirectThreadParticipantRequestsResponse) pendingRequestsValue.clone();
 | 
				
			||||||
 | 
					                clone.setUsers(filtered);
 | 
				
			||||||
 | 
					                pendingRequests.postValue(clone);
 | 
				
			||||||
 | 
					            } catch (CloneNotSupportedException e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "denyUsers: ", e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private interface OnSuccessAction {
 | 
				
			||||||
 | 
					        void onSuccess();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
 | 
					    private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
 | 
				
			||||||
                                            final Call<DirectThreadDetailsChangeResponse> addUsersRequest) {
 | 
					                                            final Call<DirectThreadDetailsChangeResponse> request) {
 | 
				
			||||||
        addUsersRequest.enqueue(new Callback<DirectThreadDetailsChangeResponse>() {
 | 
					        handleDetailsChangeRequest(data, request, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void handleDetailsChangeRequest(final MutableLiveData<Resource<Object>> data,
 | 
				
			||||||
 | 
					                                            final Call<DirectThreadDetailsChangeResponse> request,
 | 
				
			||||||
 | 
					                                            @Nullable final OnSuccessAction action) {
 | 
				
			||||||
 | 
					        request.enqueue(new Callback<DirectThreadDetailsChangeResponse>() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onResponse(@NonNull final Call<DirectThreadDetailsChangeResponse> call,
 | 
					            public void onResponse(@NonNull final Call<DirectThreadDetailsChangeResponse> call,
 | 
				
			||||||
                                   @NonNull final Response<DirectThreadDetailsChangeResponse> response) {
 | 
					                                   @NonNull final Response<DirectThreadDetailsChangeResponse> response) {
 | 
				
			||||||
@ -464,16 +528,19 @@ public class DirectSettingsViewModel extends AndroidViewModel {
 | 
				
			|||||||
                    handleErrorResponse(response, data);
 | 
					                    handleErrorResponse(response, data);
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                final DirectThreadDetailsChangeResponse addUserResponse = response.body();
 | 
					                final DirectThreadDetailsChangeResponse changeResponse = response.body();
 | 
				
			||||||
                if (addUserResponse == null) {
 | 
					                if (changeResponse == null) {
 | 
				
			||||||
                    data.postValue(Resource.error("Response is null", null));
 | 
					                    data.postValue(Resource.error("Response is null", null));
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                data.postValue(Resource.success(new Object()));
 | 
					                data.postValue(Resource.success(new Object()));
 | 
				
			||||||
                final DirectThread thread = addUserResponse.getThread();
 | 
					                final DirectThread thread = changeResponse.getThread();
 | 
				
			||||||
                if (thread != null) {
 | 
					                if (thread != null) {
 | 
				
			||||||
                    setThread(thread);
 | 
					                    setThread(thread);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                if (action != null) {
 | 
				
			||||||
 | 
					                    action.onSuccess();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
@ -578,4 +645,44 @@ public class DirectSettingsViewModel extends AndroidViewModel {
 | 
				
			|||||||
    public void setViewer(final User viewer) {
 | 
					    public void setViewer(final User viewer) {
 | 
				
			||||||
        this.viewer = viewer;
 | 
					        this.viewer = viewer;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void fetchPendingRequests() {
 | 
				
			||||||
 | 
					        final Call<DirectThreadParticipantRequestsResponse> request = directMessagesService.participantRequests(thread.getThreadId(), 5, null);
 | 
				
			||||||
 | 
					        request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onResponse(@NonNull final Call<DirectThreadParticipantRequestsResponse> call,
 | 
				
			||||||
 | 
					                                   @NonNull final Response<DirectThreadParticipantRequestsResponse> response) {
 | 
				
			||||||
 | 
					                if (!response.isSuccessful()) {
 | 
				
			||||||
 | 
					                    if (response.errorBody() != null) {
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            final String string = response.errorBody().string();
 | 
				
			||||||
 | 
					                            final String msg = String.format(Locale.US,
 | 
				
			||||||
 | 
					                                                             "onResponse: url: %s, responseCode: %d, errorBody: %s",
 | 
				
			||||||
 | 
					                                                             call.request().url().toString(),
 | 
				
			||||||
 | 
					                                                             response.code(),
 | 
				
			||||||
 | 
					                                                             string);
 | 
				
			||||||
 | 
					                            Log.e(TAG, msg);
 | 
				
			||||||
 | 
					                        } catch (IOException e) {
 | 
				
			||||||
 | 
					                            Log.e(TAG, "onResponse: ", e);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Log.e(TAG, "onResponse: request was not successful and response error body was null");
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                final DirectThreadParticipantRequestsResponse body = response.body();
 | 
				
			||||||
 | 
					                if (body == null) {
 | 
				
			||||||
 | 
					                    Log.e(TAG, "onResponse: response body was null");
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                pendingRequests.postValue(body);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onFailure(@NonNull final Call<DirectThreadParticipantRequestsResponse> call, @NonNull final Throwable t) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "onFailure: ", t);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -46,6 +46,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroa
 | 
				
			|||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponseMessageMetadata;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponseMessageMetadata;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponsePayload;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponsePayload;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
 | 
					import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
 | 
				
			||||||
import awais.instagrabber.utils.BitmapUtils;
 | 
					import awais.instagrabber.utils.BitmapUtils;
 | 
				
			||||||
import awais.instagrabber.utils.Constants;
 | 
					import awais.instagrabber.utils.Constants;
 | 
				
			||||||
@ -82,6 +83,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
 | 
				
			|||||||
    private final MutableLiveData<List<User>> users = new MutableLiveData<>(new ArrayList<>());
 | 
					    private final MutableLiveData<List<User>> users = new MutableLiveData<>(new ArrayList<>());
 | 
				
			||||||
    private final MutableLiveData<List<User>> leftUsers = new MutableLiveData<>(new ArrayList<>());
 | 
					    private final MutableLiveData<List<User>> leftUsers = new MutableLiveData<>(new ArrayList<>());
 | 
				
			||||||
    private final MutableLiveData<DirectItem> replyToItem = new MutableLiveData<>();
 | 
					    private final MutableLiveData<DirectItem> replyToItem = new MutableLiveData<>();
 | 
				
			||||||
 | 
					    private final MutableLiveData<Integer> pendingRequestsCount = new MutableLiveData<>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final DirectMessagesService service;
 | 
					    private final DirectMessagesService service;
 | 
				
			||||||
    private final ContentResolver contentResolver;
 | 
					    private final ContentResolver contentResolver;
 | 
				
			||||||
@ -89,6 +91,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
 | 
				
			|||||||
    private final String csrfToken;
 | 
					    private final String csrfToken;
 | 
				
			||||||
    private final File recordingsDir;
 | 
					    private final File recordingsDir;
 | 
				
			||||||
    private final Application application;
 | 
					    private final Application application;
 | 
				
			||||||
 | 
					    private final long viewerId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String cursor;
 | 
					    private String cursor;
 | 
				
			||||||
    private String threadId;
 | 
					    private String threadId;
 | 
				
			||||||
@ -97,7 +100,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
 | 
				
			|||||||
    private User currentUser;
 | 
					    private User currentUser;
 | 
				
			||||||
    private Call<DirectThreadFeedResponse> chatsRequest;
 | 
					    private Call<DirectThreadFeedResponse> chatsRequest;
 | 
				
			||||||
    private VoiceRecorder voiceRecorder;
 | 
					    private VoiceRecorder voiceRecorder;
 | 
				
			||||||
    private final long viewerId;
 | 
					    private boolean viewerIsAdmin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public DirectThreadViewModel(@NonNull final Application application) {
 | 
					    public DirectThreadViewModel(@NonNull final Application application) {
 | 
				
			||||||
        super(application);
 | 
					        super(application);
 | 
				
			||||||
@ -328,6 +331,10 @@ public class DirectThreadViewModel extends AndroidViewModel {
 | 
				
			|||||||
        return replyToItem;
 | 
					        return replyToItem;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public LiveData<Integer> getPendingRequestsCount() {
 | 
				
			||||||
 | 
					        return pendingRequestsCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void fetchChats() {
 | 
					    public void fetchChats() {
 | 
				
			||||||
        final Boolean isFetching = fetching.getValue();
 | 
					        final Boolean isFetching = fetching.getValue();
 | 
				
			||||||
        if ((isFetching != null && isFetching) || !hasOlder) return;
 | 
					        if ((isFetching != null && isFetching) || !hasOlder) return;
 | 
				
			||||||
@ -391,6 +398,11 @@ public class DirectThreadViewModel extends AndroidViewModel {
 | 
				
			|||||||
        users.postValue(thread.getUsers());
 | 
					        users.postValue(thread.getUsers());
 | 
				
			||||||
        leftUsers.postValue(thread.getLeftUsers());
 | 
					        leftUsers.postValue(thread.getLeftUsers());
 | 
				
			||||||
        fetching.postValue(false);
 | 
					        fetching.postValue(false);
 | 
				
			||||||
 | 
					        final List<Long> adminUserIds = thread.getAdminUserIds();
 | 
				
			||||||
 | 
					        viewerIsAdmin = adminUserIds.contains(viewerId);
 | 
				
			||||||
 | 
					        if (thread.isGroup() && viewerIsAdmin) {
 | 
				
			||||||
 | 
					            fetchPendingRequests();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public LiveData<Resource<DirectItem>> sendText(final String text) {
 | 
					    public LiveData<Resource<DirectItem>> sendText(final String text) {
 | 
				
			||||||
@ -934,7 +946,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
 | 
				
			|||||||
                                 @NonNull final MutableLiveData<Resource<DirectItem>> data,
 | 
					                                 @NonNull final MutableLiveData<Resource<DirectItem>> data,
 | 
				
			||||||
                                 @NonNull final DirectItem directItem) {
 | 
					                                 @NonNull final DirectItem directItem) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            final String string = response.errorBody().string();
 | 
					            final String string = response.errorBody() != null ? response.errorBody().string() : "";
 | 
				
			||||||
            final String msg = String.format(Locale.US,
 | 
					            final String msg = String.format(Locale.US,
 | 
				
			||||||
                                             "onResponse: url: %s, responseCode: %d, errorBody: %s",
 | 
					                                             "onResponse: url: %s, responseCode: %d, errorBody: %s",
 | 
				
			||||||
                                             call.request().url().toString(),
 | 
					                                             call.request().url().toString(),
 | 
				
			||||||
@ -1047,4 +1059,44 @@ public class DirectThreadViewModel extends AndroidViewModel {
 | 
				
			|||||||
        // Log.d(TAG, "setReplyToItem: " + item);
 | 
					        // Log.d(TAG, "setReplyToItem: " + item);
 | 
				
			||||||
        replyToItem.postValue(item);
 | 
					        replyToItem.postValue(item);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void fetchPendingRequests() {
 | 
				
			||||||
 | 
					        final Call<DirectThreadParticipantRequestsResponse> request = service.participantRequests(threadId, 1, null);
 | 
				
			||||||
 | 
					        request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onResponse(@NonNull final Call<DirectThreadParticipantRequestsResponse> call,
 | 
				
			||||||
 | 
					                                   @NonNull final Response<DirectThreadParticipantRequestsResponse> response) {
 | 
				
			||||||
 | 
					                if (!response.isSuccessful()) {
 | 
				
			||||||
 | 
					                    if (response.errorBody() != null) {
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            final String string = response.errorBody().string();
 | 
				
			||||||
 | 
					                            final String msg = String.format(Locale.US,
 | 
				
			||||||
 | 
					                                                             "onResponse: url: %s, responseCode: %d, errorBody: %s",
 | 
				
			||||||
 | 
					                                                             call.request().url().toString(),
 | 
				
			||||||
 | 
					                                                             response.code(),
 | 
				
			||||||
 | 
					                                                             string);
 | 
				
			||||||
 | 
					                            Log.e(TAG, msg);
 | 
				
			||||||
 | 
					                        } catch (IOException e) {
 | 
				
			||||||
 | 
					                            Log.e(TAG, "onResponse: ", e);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Log.e(TAG, "onResponse: request was not successful and response error body was null");
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                final DirectThreadParticipantRequestsResponse body = response.body();
 | 
				
			||||||
 | 
					                if (body == null) {
 | 
				
			||||||
 | 
					                    Log.e(TAG, "onResponse: response body was null");
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                pendingRequestsCount.postValue(body.getTotalParticipantRequests());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onFailure(@NonNull final Call<DirectThreadParticipantRequestsResponse> call, @NonNull final Throwable t) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "onFailure: ", t);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThread;
 | 
				
			|||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
 | 
				
			||||||
 | 
					import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
 | 
				
			||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
 | 
					import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
 | 
				
			||||||
import awais.instagrabber.utils.TextUtils;
 | 
					import awais.instagrabber.utils.TextUtils;
 | 
				
			||||||
import awais.instagrabber.utils.Utils;
 | 
					import awais.instagrabber.utils.Utils;
 | 
				
			||||||
@ -349,4 +350,31 @@ public class DirectMessagesService extends BaseService {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
        return repository.unmuteMentions(threadId, form);
 | 
					        return repository.unmuteMentions(threadId, form);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Call<DirectThreadParticipantRequestsResponse> participantRequests(@NonNull final String threadId,
 | 
				
			||||||
 | 
					                                                                             final int pageSize,
 | 
				
			||||||
 | 
					                                                                             @Nullable final String cursor) {
 | 
				
			||||||
 | 
					        return repository.participantRequests(threadId, pageSize, cursor);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Call<DirectThreadDetailsChangeResponse> approveParticipantRequests(@NonNull final String threadId,
 | 
				
			||||||
 | 
					                                                                              @NonNull final List<Long> userIds) {
 | 
				
			||||||
 | 
					        final ImmutableMap<String, String> form = ImmutableMap.of(
 | 
				
			||||||
 | 
					                "_csrftoken", csrfToken,
 | 
				
			||||||
 | 
					                "_uuid", deviceUuid,
 | 
				
			||||||
 | 
					                "user_ids", new JSONArray(userIds).toString()
 | 
				
			||||||
 | 
					                // , "share_join_chat_story", String.valueOf(true)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        return repository.approveParticipantRequests(threadId, form);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Call<DirectThreadDetailsChangeResponse> declineParticipantRequests(@NonNull final String threadId,
 | 
				
			||||||
 | 
					                                                                              @NonNull final List<Long> userIds) {
 | 
				
			||||||
 | 
					        final ImmutableMap<String, String> form = ImmutableMap.of(
 | 
				
			||||||
 | 
					                "_csrftoken", csrfToken,
 | 
				
			||||||
 | 
					                "_uuid", deviceUuid,
 | 
				
			||||||
 | 
					                "user_ids", new JSONArray(userIds).toString()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        return repository.declineParticipantRequests(threadId, form);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -101,10 +101,37 @@
 | 
				
			|||||||
                    android:layout_marginEnd="8dp"
 | 
					                    android:layout_marginEnd="8dp"
 | 
				
			||||||
                    android:paddingStart="0dp"
 | 
					                    android:paddingStart="0dp"
 | 
				
			||||||
                    android:paddingEnd="8dp"
 | 
					                    android:paddingEnd="8dp"
 | 
				
			||||||
                    app:layout_constraintBottom_toTopOf="@id/leave"
 | 
					                    app:layout_constraintBottom_toTopOf="@id/approval_required"
 | 
				
			||||||
                    app:layout_constraintEnd_toEndOf="parent"
 | 
					                    app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
                    app:layout_constraintTop_toBottomOf="@id/mute_messages" />
 | 
					                    app:layout_constraintTop_toBottomOf="@id/mute_messages" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <androidx.appcompat.widget.AppCompatTextView
 | 
				
			||||||
 | 
					                    android:id="@+id/approval_required_label"
 | 
				
			||||||
 | 
					                    android:layout_width="0dp"
 | 
				
			||||||
 | 
					                    android:layout_height="0dp"
 | 
				
			||||||
 | 
					                    android:gravity="center_vertical"
 | 
				
			||||||
 | 
					                    android:paddingStart="16dp"
 | 
				
			||||||
 | 
					                    android:paddingEnd="16dp"
 | 
				
			||||||
 | 
					                    android:text="@string/approval_required_for_new_members"
 | 
				
			||||||
 | 
					                    android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
 | 
				
			||||||
 | 
					                    android:textColor="?android:textColorPrimary"
 | 
				
			||||||
 | 
					                    app:layout_constraintBottom_toBottomOf="@id/approval_required"
 | 
				
			||||||
 | 
					                    app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					                    app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					                    app:layout_constraintTop_toTopOf="@id/approval_required" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <com.google.android.material.switchmaterial.SwitchMaterial
 | 
				
			||||||
 | 
					                    android:id="@+id/approval_required"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_marginTop="4dp"
 | 
				
			||||||
 | 
					                    android:layout_marginEnd="8dp"
 | 
				
			||||||
 | 
					                    android:paddingStart="0dp"
 | 
				
			||||||
 | 
					                    android:paddingEnd="8dp"
 | 
				
			||||||
 | 
					                    app:layout_constraintBottom_toTopOf="@id/leave"
 | 
				
			||||||
 | 
					                    app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					                    app:layout_constraintTop_toBottomOf="@id/mute_mentions" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <androidx.appcompat.widget.AppCompatTextView
 | 
					                <androidx.appcompat.widget.AppCompatTextView
 | 
				
			||||||
                    android:id="@+id/leave"
 | 
					                    android:id="@+id/leave"
 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
@ -119,8 +146,44 @@
 | 
				
			|||||||
                    android:text="@string/dms_action_leave"
 | 
					                    android:text="@string/dms_action_leave"
 | 
				
			||||||
                    android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
 | 
					                    android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
 | 
				
			||||||
                    android:textColor="@color/red_600"
 | 
					                    android:textColor="@color/red_600"
 | 
				
			||||||
 | 
					                    app:layout_constraintBottom_toTopOf="@id/pending_members_header"
 | 
				
			||||||
 | 
					                    app:layout_constraintTop_toBottomOf="@id/approval_required" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <androidx.appcompat.widget.AppCompatTextView
 | 
				
			||||||
 | 
					                    android:id="@+id/pending_members_header"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:paddingLeft="16dp"
 | 
				
			||||||
 | 
					                    android:paddingTop="16dp"
 | 
				
			||||||
 | 
					                    android:paddingRight="16dp"
 | 
				
			||||||
 | 
					                    android:paddingBottom="8dp"
 | 
				
			||||||
 | 
					                    android:text="@string/requests"
 | 
				
			||||||
 | 
					                    android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
 | 
				
			||||||
 | 
					                    android:textColor="?colorAccent"
 | 
				
			||||||
 | 
					                    android:textStyle="bold"
 | 
				
			||||||
 | 
					                    app:layout_constraintBottom_toTopOf="@id/pending_members"
 | 
				
			||||||
 | 
					                    app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					                    app:layout_constraintTop_toBottomOf="@id/leave" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <androidx.appcompat.widget.AppCompatTextView
 | 
				
			||||||
 | 
					                    android:id="@+id/pending_members_admin_only"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:paddingLeft="16dp"
 | 
				
			||||||
 | 
					                    android:paddingRight="16dp"
 | 
				
			||||||
 | 
					                    android:text="@string/admins_only"
 | 
				
			||||||
 | 
					                    android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
 | 
				
			||||||
 | 
					                    app:layout_constraintBaseline_toBaselineOf="@id/pending_members_header"
 | 
				
			||||||
 | 
					                    app:layout_constraintEnd_toEndOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
 | 
					                    android:id="@+id/pending_members"
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
                    app:layout_constraintBottom_toTopOf="@id/add_members"
 | 
					                    app:layout_constraintBottom_toTopOf="@id/add_members"
 | 
				
			||||||
                    app:layout_constraintTop_toBottomOf="@id/mute_mentions" />
 | 
					                    app:layout_constraintTop_toBottomOf="@id/pending_members_header"
 | 
				
			||||||
 | 
					                    tools:itemCount="2"
 | 
				
			||||||
 | 
					                    tools:listitem="@layout/layout_dm_user_item" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <androidx.appcompat.widget.AppCompatTextView
 | 
					                <androidx.appcompat.widget.AppCompatTextView
 | 
				
			||||||
                    android:id="@+id/add_members"
 | 
					                    android:id="@+id/add_members"
 | 
				
			||||||
@ -139,13 +202,21 @@
 | 
				
			|||||||
                    app:drawableStartCompat="@drawable/ic_add"
 | 
					                    app:drawableStartCompat="@drawable/ic_add"
 | 
				
			||||||
                    app:drawableTint="?android:textColorPrimary"
 | 
					                    app:drawableTint="?android:textColorPrimary"
 | 
				
			||||||
                    app:layout_constraintBottom_toBottomOf="parent"
 | 
					                    app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
                    app:layout_constraintTop_toBottomOf="@id/leave" />
 | 
					                    app:layout_constraintTop_toBottomOf="@id/pending_members" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <androidx.constraintlayout.widget.Group
 | 
				
			||||||
 | 
					                    android:id="@+id/pending_members_group"
 | 
				
			||||||
 | 
					                    android:layout_width="0dp"
 | 
				
			||||||
 | 
					                    android:layout_height="0dp"
 | 
				
			||||||
 | 
					                    android:visibility="gone"
 | 
				
			||||||
 | 
					                    app:constraint_referenced_ids="pending_members,pending_members_admin_only,pending_members_header"
 | 
				
			||||||
 | 
					                    tools:visibility="visible" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <androidx.constraintlayout.widget.Group
 | 
					                <androidx.constraintlayout.widget.Group
 | 
				
			||||||
                    android:id="@+id/group_settings"
 | 
					                    android:id="@+id/group_settings"
 | 
				
			||||||
                    android:layout_width="0dp"
 | 
					                    android:layout_width="0dp"
 | 
				
			||||||
                    android:layout_height="0dp"
 | 
					                    android:layout_height="0dp"
 | 
				
			||||||
                    app:constraint_referenced_ids="title_edit_input_layout, mute_mentions_label, mute_mentions, leave, add_members" />
 | 
					                    app:constraint_referenced_ids="title_edit_input_layout, mute_mentions_label, mute_mentions, leave, add_members, approval_required, approval_required_label" />
 | 
				
			||||||
            </androidx.constraintlayout.widget.ConstraintLayout>
 | 
					            </androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </com.google.android.material.appbar.CollapsingToolbarLayout>
 | 
					        </com.google.android.material.appbar.CollapsingToolbarLayout>
 | 
				
			||||||
@ -155,5 +226,6 @@
 | 
				
			|||||||
        android:id="@+id/users"
 | 
					        android:id="@+id/users"
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 | 
					        app:layout_behavior="@string/appbar_scrolling_view_behavior"
 | 
				
			||||||
 | 
					        tools:listitem="@layout/layout_dm_user_item" />
 | 
				
			||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
 | 
					</androidx.coordinatorlayout.widget.CoordinatorLayout>
 | 
				
			||||||
							
								
								
									
										118
									
								
								app/src/main/res/layout/layout_dm_pending_user_item.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								app/src/main/res/layout/layout_dm_pending_user_item.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<androidx.constraintlayout.widget.ConstraintLayout 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="72dp"
 | 
				
			||||||
 | 
					    android:background="?android:selectableItemBackground"
 | 
				
			||||||
 | 
					    android:clickable="true"
 | 
				
			||||||
 | 
					    android:focusable="true"
 | 
				
			||||||
 | 
					    android:orientation="horizontal"
 | 
				
			||||||
 | 
					    android:paddingStart="16dp"
 | 
				
			||||||
 | 
					    android:paddingEnd="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <com.facebook.drawee.view.SimpleDraweeView
 | 
				
			||||||
 | 
					        android:id="@+id/profile_pic"
 | 
				
			||||||
 | 
					        android:layout_width="@dimen/dm_inbox_avatar_size"
 | 
				
			||||||
 | 
					        android:layout_height="@dimen/dm_inbox_avatar_size"
 | 
				
			||||||
 | 
					        app:actualImageScaleType="centerCrop"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toStartOf="@id/username"
 | 
				
			||||||
 | 
					        app:layout_constraintHorizontal_chainStyle="spread_inside"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:roundAsCircle="true"
 | 
				
			||||||
 | 
					        tools:background="@mipmap/ic_launcher" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <androidx.appcompat.widget.AppCompatTextView
 | 
				
			||||||
 | 
					        android:id="@+id/username"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_marginStart="16dp"
 | 
				
			||||||
 | 
					        android:ellipsize="end"
 | 
				
			||||||
 | 
					        android:gravity="bottom"
 | 
				
			||||||
 | 
					        android:singleLine="true"
 | 
				
			||||||
 | 
					        android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toTopOf="@id/requester"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toStartOf="@id/controls_barrier"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toEndOf="@id/profile_pic"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintVertical_chainStyle="packed"
 | 
				
			||||||
 | 
					        tools:text="username......................." />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <androidx.appcompat.widget.AppCompatTextView
 | 
				
			||||||
 | 
					        android:id="@+id/requester"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:ellipsize="end"
 | 
				
			||||||
 | 
					        android:maxLines="1"
 | 
				
			||||||
 | 
					        android:textAppearance="@style/TextAppearance.AppCompat.Caption"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="@id/username"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="@id/username"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toBottomOf="@id/username"
 | 
				
			||||||
 | 
					        tools:text="Added by someone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <androidx.constraintlayout.widget.Barrier
 | 
				
			||||||
 | 
					        android:id="@+id/controls_barrier"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        app:barrierAllowsGoneWidgets="true"
 | 
				
			||||||
 | 
					        app:barrierDirection="start"
 | 
				
			||||||
 | 
					        app:constraint_referenced_ids="progress,approve,deny" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <androidx.appcompat.widget.AppCompatImageView
 | 
				
			||||||
 | 
					        android:id="@+id/approve"
 | 
				
			||||||
 | 
					        android:layout_width="42dp"
 | 
				
			||||||
 | 
					        android:layout_height="42dp"
 | 
				
			||||||
 | 
					        android:layout_marginStart="4dp"
 | 
				
			||||||
 | 
					        android:background="?selectableItemBackgroundBorderless"
 | 
				
			||||||
 | 
					        android:scaleType="centerInside"
 | 
				
			||||||
 | 
					        android:visibility="visible"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toStartOf="@id/deny"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toEndOf="@id/controls_barrier"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:srcCompat="@drawable/ic_check_24"
 | 
				
			||||||
 | 
					        app:tint="@color/green_500"
 | 
				
			||||||
 | 
					        tools:visibility="visible" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <androidx.appcompat.widget.AppCompatImageView
 | 
				
			||||||
 | 
					        android:id="@+id/deny"
 | 
				
			||||||
 | 
					        android:layout_width="42dp"
 | 
				
			||||||
 | 
					        android:layout_height="42dp"
 | 
				
			||||||
 | 
					        android:layout_marginStart="8dp"
 | 
				
			||||||
 | 
					        android:background="?selectableItemBackgroundBorderless"
 | 
				
			||||||
 | 
					        android:scaleType="centerInside"
 | 
				
			||||||
 | 
					        android:visibility="visible"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="@id/profile_pic"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toEndOf="@id/approve"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="@id/profile_pic"
 | 
				
			||||||
 | 
					        app:srcCompat="@drawable/ic_close_24"
 | 
				
			||||||
 | 
					        app:tint="@color/red_500"
 | 
				
			||||||
 | 
					        tools:visibility="visible" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <com.google.android.material.progressindicator.CircularProgressIndicator
 | 
				
			||||||
 | 
					        android:id="@+id/progress"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:indeterminate="true"
 | 
				
			||||||
 | 
					        android:visibility="gone"
 | 
				
			||||||
 | 
					        app:indicatorColor="?colorSurface"
 | 
				
			||||||
 | 
					        app:indicatorSize="30dp"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="@id/profile_pic"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toEndOf="@id/controls_barrier"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="@id/profile_pic"
 | 
				
			||||||
 | 
					        app:trackColor="@color/blue_900"
 | 
				
			||||||
 | 
					        tools:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <include
 | 
				
			||||||
 | 
					        layout="@layout/item_pref_divider"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="1dp"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="@id/thread_title" />
 | 
				
			||||||
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
@ -9,12 +9,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <action
 | 
					    <action
 | 
				
			||||||
        android:id="@+id/action_global_profileFragment"
 | 
					        android:id="@+id/action_global_profileFragment"
 | 
				
			||||||
        app:destination="@id/profile_nav_graph">
 | 
					        app:destination="@id/profile_nav_graph" />
 | 
				
			||||||
        <argument
 | 
					 | 
				
			||||||
            android:name="username"
 | 
					 | 
				
			||||||
            app:argType="string"
 | 
					 | 
				
			||||||
            app:nullable="true" />
 | 
					 | 
				
			||||||
    </action>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <include app:graph="@navigation/location_nav_graph" />
 | 
					    <include app:graph="@navigation/location_nav_graph" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -407,4 +407,10 @@
 | 
				
			|||||||
    <string name="video">Video</string>
 | 
					    <string name="video">Video</string>
 | 
				
			||||||
    <string name="voice_message">Voice message</string>
 | 
					    <string name="voice_message">Voice message</string>
 | 
				
			||||||
    <string name="post">Post</string>
 | 
					    <string name="post">Post</string>
 | 
				
			||||||
 | 
					    <string name="approval_required_for_new_members">Approval required to join</string>
 | 
				
			||||||
 | 
					    <string name="requests">Requests</string>
 | 
				
			||||||
 | 
					    <string name="admins_only">Admins only</string>
 | 
				
			||||||
 | 
					    <string name="added_by">Added by %s</string>
 | 
				
			||||||
 | 
					    <string name="admin_approval_required">Admin approval required</string>
 | 
				
			||||||
 | 
					    <string name="admin_approval_required_description">An admin approval will be required to add new members to the group</string>
 | 
				
			||||||
</resources>
 | 
					</resources>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user