Update conversation list multi-select to use checkboxes.

This commit is contained in:
Greyson Parrelli
2021-10-15 15:46:27 -04:00
parent 2e2b4e1406
commit 26e04ce6d2
8 changed files with 130 additions and 46 deletions

View File

@@ -17,12 +17,8 @@
package org.thoughtcrime.securesms.conversationlist;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@@ -46,7 +42,6 @@ import androidx.lifecycle.Transformations;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.makeramen.roundedimageview.RoundedDrawable;
import org.signal.core.util.DimensionUnit;
@@ -122,6 +117,9 @@ public final class ConversationListItem extends ConstraintLayout
private Locale locale;
private String highlightSubstring;
private BadgeImageView badge;
private View checkContainer;
private View uncheckedView;
private View checkedView;
private int unreadCount;
private AvatarImageView contactPhotoImage;
@@ -151,6 +149,11 @@ public final class ConversationListItem extends ConstraintLayout
this.archivedView = findViewById(R.id.conversation_list_item_archived);
this.unreadIndicator = findViewById(R.id.conversation_list_item_unread_indicator);
this.badge = findViewById(R.id.conversation_list_item_badge);
this.checkContainer = findViewById(R.id.conversation_list_item_check_container);
this.uncheckedView = findViewById(R.id.conversation_list_item_unchecked);
this.checkedView = findViewById(R.id.conversation_list_item_checked);
getLayoutTransition().setDuration(150);
}
@Override
@@ -161,16 +164,16 @@ public final class ConversationListItem extends ConstraintLayout
@NonNull Set<Long> selectedThreads,
boolean batchMode)
{
bind(thread, glideRequests, locale, typingThreads, selectedThreads, batchMode, null);
bindThread(thread, glideRequests, locale, typingThreads, selectedThreads, batchMode, null);
}
public void bind(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull Set<Long> selectedThreads,
boolean batchMode,
@Nullable String highlightSubstring)
public void bindThread(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull Set<Long> selectedThreads,
boolean batchMode,
@Nullable String highlightSubstring)
{
observeRecipient(thread.getRecipient().live());
observeDisplayBody(null);
@@ -214,16 +217,15 @@ public final class ConversationListItem extends ConstraintLayout
setStatusIcons(thread);
setBatchMode(batchMode);
setRippleColor(recipient.get());
badge.setBadgeFromRecipient(recipient.get());
setUnreadIndicator(thread);
this.contactPhotoImage.setAvatar(glideRequests, recipient.get(), !batchMode);
}
public void bind(@NonNull Recipient contact,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@Nullable String highlightSubstring)
public void bindContact(@NonNull Recipient contact,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@Nullable String highlightSubstring)
{
observeRecipient(contact.live());
observeDisplayBody(null);
@@ -243,15 +245,14 @@ public final class ConversationListItem extends ConstraintLayout
alertView.setNone();
setBatchMode(false);
setRippleColor(contact);
badge.setBadgeFromRecipient(recipient.get());
contactPhotoImage.setAvatar(glideRequests, recipient.get(), !batchMode);
}
public void bind(@NonNull MessageResult messageResult,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@Nullable String highlightSubstring)
public void bindMessage(@NonNull MessageResult messageResult,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@Nullable String highlightSubstring)
{
observeRecipient(messageResult.getConversationRecipient().live());
observeDisplayBody(null);
@@ -271,7 +272,6 @@ public final class ConversationListItem extends ConstraintLayout
alertView.setNone();
setBatchMode(false);
setRippleColor(recipient.get());
badge.setBadgeFromRecipient(recipient.get());
contactPhotoImage.setAvatar(glideRequests, recipient.get(), !batchMode);
}
@@ -290,7 +290,23 @@ public final class ConversationListItem extends ConstraintLayout
@Override
public void setBatchMode(boolean batchMode) {
this.batchMode = batchMode;
setSelected(batchMode && selectedThreads.contains(thread.getThreadId()));
boolean selected = batchMode && selectedThreads.contains(thread.getThreadId());
setSelected(selected);
if (batchMode && selected) {
checkContainer.setVisibility(VISIBLE);
uncheckedView.setVisibility(GONE);
checkedView.setVisibility(VISIBLE);
} else if (batchMode) {
checkContainer.setVisibility(VISIBLE);
uncheckedView.setVisibility(VISIBLE);
checkedView.setVisibility(GONE);
} else {
checkContainer.setVisibility(GONE);
uncheckedView.setVisibility(GONE);
checkedView.setVisibility(GONE);
}
}
@Override
@@ -402,13 +418,6 @@ public final class ConversationListItem extends ConstraintLayout
}
}
private void setRippleColor(Recipient recipient) {
if (Build.VERSION.SDK_INT >= 21) {
((RippleDrawable)(getBackground()).mutate())
.setColor(ColorStateList.valueOf(recipient.getChatColors().asSingleColor()));
}
}
private void setUnreadIndicator(ThreadRecord thread) {
if ((thread.isOutgoing() && !thread.isForcedUnread()) || thread.isRead()) {
unreadIndicator.setVisibility(View.GONE);
@@ -433,7 +442,6 @@ public final class ConversationListItem extends ConstraintLayout
fromView.setText(recipient, false);
}
contactPhotoImage.setAvatar(glideRequests, recipient, !batchMode);
setRippleColor(recipient);
badge.setBadgeFromRecipient(recipient);
}

View File

@@ -162,7 +162,7 @@ class ConversationListSearchAdapter extends RecyclerView.Adapter<Conversation
@NonNull Locale locale,
@Nullable String query)
{
root.bind(conversationResult, glideRequests, locale, Collections.emptySet(), Collections.emptySet(), false, query);
root.bindThread(conversationResult, glideRequests, locale, Collections.emptySet(), Collections.emptySet(), false, query);
root.setOnClickListener(view -> eventListener.onConversationClicked(conversationResult));
}
@@ -172,7 +172,7 @@ class ConversationListSearchAdapter extends RecyclerView.Adapter<Conversation
@NonNull Locale locale,
@Nullable String query)
{
root.bind(contactResult, glideRequests, locale, query);
root.bindContact(contactResult, glideRequests, locale, query);
root.setOnClickListener(view -> eventListener.onContactClicked(contactResult));
}
@@ -182,7 +182,7 @@ class ConversationListSearchAdapter extends RecyclerView.Adapter<Conversation
@NonNull Locale locale,
@Nullable String query)
{
root.bind(messageResult, glideRequests, locale, query);
root.bindMessage(messageResult, glideRequests, locale, query);
root.setOnClickListener(view -> eventListener.onMessageClicked(messageResult));
}

View File

@@ -1,10 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/core_ultramarine">
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
<item>
<selector>
<item android:drawable="@color/core_ultramarine_33" android:state_selected="true" />
</selector>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
android:enterFadeDuration="100"
android:exitFadeDuration="100">
<item android:state_selected="true">
<inset
android:insetLeft="12dp"
android:insetRight="12dp"
android:insetTop="2dp"
android:insetBottom="2dp">
<shape android:shape="rectangle">
<solid android:color="@color/conversation_list_selected_color" />
<corners android:radius="18dp" />
</shape>
</inset>
</item>
</ripple>
<item>
<ripple android:color="@color/conversation_list_selected_color">
<item android:id="@android:id/mask">
<inset
android:insetLeft="12dp"
android:insetRight="12dp"
android:insetTop="2dp"
android:insetBottom="2dp">
<shape android:shape="rectangle">
<solid android:color="@color/transparent_black_60" />
<corners android:radius="18dp" />
</shape>
</inset>
</item>
</ripple>
</item>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/conversation_list_selected_color">
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
</ripple>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke
android:width="2dp"
android:color="@color/core_grey_25" />
</shape>

View File

@@ -10,17 +10,48 @@
android:nextFocusLeft="@+id/container"
android:nextFocusRight="@+id/fab"
android:paddingStart="@dimen/dsl_settings_gutter"
android:paddingEnd="@dimen/dsl_settings_gutter">
android:paddingEnd="@dimen/dsl_settings_gutter"
android:animateLayoutChanges="true">
<FrameLayout
android:id="@+id/conversation_list_item_check_container"
android:layout_width="24dp"
android:layout_height="24dp"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:id="@+id/conversation_list_item_unchecked"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srcCompat="@drawable/multiselect_empty_ring"
android:visibility="gone"/>
<ImageView
android:id="@+id/conversation_list_item_checked"
android:layout_width="24dp"
android:layout_height="24dp"
app:srcCompat="@drawable/ic_check_circle_solid_24"
app:tint="@color/signal_accent_primary"
android:visibility="gone"
tools:visibility="visible" />
</FrameLayout>
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/conversation_list_item_avatar"
android:layout_width="@dimen/conversation_list_avatar_size"
android:layout_height="@dimen/conversation_list_avatar_size"
android:layout_marginTop="12dp"
android:layout_marginStart="20dp"
android:contentDescription="@string/conversation_list_item_view__contact_photo_image"
android:foreground="@drawable/contact_photo_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/conversation_list_item_check_container"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginStart="0dp"
tools:src="@drawable/ic_contact_picture" />
<org.thoughtcrime.securesms.components.FromTextView

View File

@@ -98,6 +98,7 @@
<color name="conversation_scroll_to_bottom_foreground_color">@color/core_white</color>
<color name="conversation_list_camera_button_background">@color/core_grey_85</color>
<color name="conversation_list_selected_color">#4c6191f3</color>
<color name="camera_icon_background_tint">@color/core_grey_75</color>

View File

@@ -98,6 +98,7 @@
<color name="conversation_scroll_to_bottom_foreground_color">@color/grey_600</color>
<color name="conversation_list_camera_button_background">@color/core_white</color>
<color name="conversation_list_selected_color">#222c6bed</color>
<color name="camera_icon_background_tint">@color/core_grey_02</color>