Ammar Githam
4 years ago
16 changed files with 978 additions and 33 deletions
-
4app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
117app/src/main/java/awais/instagrabber/adapters/DirectPendingUsersAdapter.java
-
89app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectPendingUserViewHolder.java
-
103app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java
-
150app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
-
42app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
17app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.java
-
9app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThread.java
-
66app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThreadParticipantRequestsResponse.java
-
117app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java
-
56app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
-
28app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.java
-
82app/src/main/res/layout/fragment_direct_messages_settings.xml
-
118app/src/main/res/layout/layout_dm_pending_user_item.xml
-
7app/src/main/res/navigation/direct_messages_nav_graph.xml
-
6app/src/main/res/values/strings.xml
@ -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); |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue