mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-14 23:18:43 +00:00
Show member labels in conversation settings.
This commit is contained in:
committed by
Greyson Parrelli
parent
0199cd24ef
commit
d7b7727aa6
@@ -33,6 +33,7 @@ import org.signal.core.util.Result
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
import org.signal.core.util.concurrent.addTo
|
||||
import org.signal.core.util.getParcelableArrayListExtraCompat
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.thoughtcrime.securesms.AvatarPreviewActivity
|
||||
import org.thoughtcrime.securesms.BlockUnblockDialog
|
||||
@@ -67,10 +68,13 @@ import org.thoughtcrime.securesms.components.settings.conversation.preferences.R
|
||||
import org.thoughtcrime.securesms.components.settings.conversation.preferences.SharedMediaPreference
|
||||
import org.thoughtcrime.securesms.components.settings.conversation.preferences.Utils.formatMutedUntil
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.colors.Colorizer
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.StyledMemberLabel
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry
|
||||
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog
|
||||
import org.thoughtcrime.securesms.groups.ui.addmembers.AddMembersActivity
|
||||
import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupsActivity
|
||||
@@ -123,6 +127,7 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
private val args: ConversationSettingsFragmentArgs by navArgs()
|
||||
private val alertTint by lazy { ContextCompat.getColor(requireContext(), R.color.signal_alert_primary) }
|
||||
private val alertDisabledTint by lazy { ContextCompat.getColor(requireContext(), R.color.signal_alert_primary_50) }
|
||||
private val colorizer = Colorizer()
|
||||
private val blockIcon by lazy {
|
||||
ContextUtil.requireDrawable(requireContext(), R.drawable.symbol_block_24).apply {
|
||||
colorFilter = PorterDuffColorFilter(alertTint, PorterDuff.Mode.SRC_IN)
|
||||
@@ -199,7 +204,9 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
|
||||
REQUEST_CODE_RETURN_FROM_MEDIA -> viewModel.refreshSharedMedia()
|
||||
|
||||
REQUEST_CODE_ADD_CONTACT -> viewModel.refreshRecipient()
|
||||
|
||||
REQUEST_CODE_VIEW_CONTACT -> viewModel.refreshRecipient()
|
||||
}
|
||||
}
|
||||
@@ -432,7 +439,13 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), CoreUiR.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, getParentFragmentManager())
|
||||
.withPermanentDenialDialog(
|
||||
getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos),
|
||||
null,
|
||||
R.string.CameraXFragment_allow_access_camera,
|
||||
R.string.CameraXFragment_to_capture_photos_videos,
|
||||
getParentFragmentManager()
|
||||
)
|
||||
.onAllGranted { addToGroupStoryDelegate.addToStory(state.recipient.id) }
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
@@ -768,11 +781,16 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
)
|
||||
}
|
||||
|
||||
colorizer.onGroupMembershipChanged(
|
||||
serviceIds = groupState.allMembers.mapNotNull { it.member.serviceId.orNull() }
|
||||
)
|
||||
|
||||
for (member in groupState.members) {
|
||||
customPref(
|
||||
RecipientPreference.Model(
|
||||
recipient = member.member,
|
||||
isAdmin = member.isAdmin,
|
||||
memberLabel = member.getMemberLabel(groupState),
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
onClick = {
|
||||
RecipientBottomSheetDialogFragment.show(parentFragmentManager, member.member.id, groupState.groupId)
|
||||
@@ -949,6 +967,15 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
}
|
||||
|
||||
private fun GroupMemberEntry.FullMember.getMemberLabel(
|
||||
groupState: SpecificSettingsState.GroupSettingsState
|
||||
): StyledMemberLabel? {
|
||||
return groupState.memberLabelsByRecipientId[member.id]?.let { label ->
|
||||
val tintColor = colorizer.getIncomingGroupSenderColor(context = requireContext(), recipient = member)
|
||||
StyledMemberLabel(label, tintColor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDisappearingMessagesLifespan(disappearingMessagesLifespan: Int): String {
|
||||
return if (disappearingMessagesLifespan <= 0) {
|
||||
getString(R.string.preferences_off)
|
||||
|
||||
@@ -7,8 +7,10 @@ import org.thoughtcrime.securesms.database.MediaTable
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabel
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
data class ConversationSettingsState(
|
||||
val threadId: Long = -1,
|
||||
@@ -81,7 +83,8 @@ sealed class SpecificSettingsState {
|
||||
val groupLinkEnabled: Boolean = false,
|
||||
val membershipCountDescription: String = "",
|
||||
val legacyGroupState: LegacyGroupPreference.State = LegacyGroupPreference.State.NONE,
|
||||
val isAnnouncementGroup: Boolean = false
|
||||
val isAnnouncementGroup: Boolean = false,
|
||||
val memberLabelsByRecipientId: Map<RecipientId, MemberLabel> = emptyMap()
|
||||
) : SpecificSettingsState() {
|
||||
|
||||
override val isLoaded: Boolean = groupTitleLoaded && groupDescriptionLoaded
|
||||
|
||||
@@ -31,7 +31,9 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.LiveGroup
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelRepository
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupAddMembersResult
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository
|
||||
@@ -358,6 +360,10 @@ sealed class ConversationSettingsViewModel(
|
||||
val groupState = state.requireGroupSettingsState()
|
||||
val canShowMore = !groupState.groupMembersExpanded && fullMembers.size > 6
|
||||
|
||||
if (groupId.isV2) {
|
||||
loadMemberLabels(groupId.requireV2(), fullMembers)
|
||||
}
|
||||
|
||||
state.copy(
|
||||
specificSettingsState = groupState.copy(
|
||||
allMembers = fullMembers,
|
||||
@@ -501,6 +507,19 @@ sealed class ConversationSettingsViewModel(
|
||||
override fun unblock() {
|
||||
repository.unblock(groupId)
|
||||
}
|
||||
|
||||
private fun loadMemberLabels(v2GroupId: GroupId.V2, groupMembers: List<GroupMemberEntry.FullMember>) = viewModelScope.launch(SignalDispatchers.IO) {
|
||||
val labelsByRecipientId = MemberLabelRepository.instance
|
||||
.getLabels(v2GroupId, groupMembers.map { it.member })
|
||||
|
||||
store.update { state ->
|
||||
state.copy(
|
||||
specificSettingsState = state.requireGroupSettingsState().copy(
|
||||
memberLabelsByRecipientId = labelsByRecipientId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.components.settings.conversation.preferences
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
@@ -10,6 +12,8 @@ import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelPillView
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.StyledMemberLabel
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.ContextUtil
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
@@ -20,7 +24,7 @@ import org.thoughtcrime.securesms.util.visible
|
||||
import org.signal.core.ui.R as CoreUiR
|
||||
|
||||
/**
|
||||
* Renders a Recipient as a row item with an icon, avatar, status, and admin state
|
||||
* Renders a Recipient as a row item with an icon, avatar, label/status, and admin state.
|
||||
*/
|
||||
object RecipientPreference {
|
||||
|
||||
@@ -31,6 +35,7 @@ object RecipientPreference {
|
||||
class Model(
|
||||
val recipient: Recipient,
|
||||
val isAdmin: Boolean = false,
|
||||
val memberLabel: StyledMemberLabel? = null,
|
||||
val lifecycleOwner: LifecycleOwner? = null,
|
||||
val onClick: (() -> Unit)? = null
|
||||
) : PreferenceModel<Model>() {
|
||||
@@ -41,7 +46,8 @@ object RecipientPreference {
|
||||
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||
return super.areContentsTheSame(newItem) &&
|
||||
recipient.hasSameContent(newItem.recipient) &&
|
||||
isAdmin == newItem.isAdmin
|
||||
isAdmin == newItem.isAdmin &&
|
||||
memberLabel == newItem.memberLabel
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,15 +55,14 @@ object RecipientPreference {
|
||||
private val avatar: AvatarImageView = itemView.findViewById(R.id.recipient_avatar)
|
||||
private val name: TextView = itemView.findViewById(R.id.recipient_name)
|
||||
private val about: TextView? = itemView.findViewById(R.id.recipient_about)
|
||||
private val memberLabelView: MemberLabelPillView? = itemView.findViewById(R.id.recipient_member_label)
|
||||
private val admin: View? = itemView.findViewById(R.id.admin)
|
||||
private val badge: BadgeImageView = itemView.findViewById(R.id.recipient_badge)
|
||||
|
||||
private var recipient: Recipient? = null
|
||||
|
||||
private val recipientObserver = object : Observer<Recipient> {
|
||||
override fun onChanged(recipient: Recipient) {
|
||||
onRecipientChanged(recipient)
|
||||
}
|
||||
private val recipientObserver = Observer<Recipient> { recipient ->
|
||||
onRecipientChanged(recipient)
|
||||
}
|
||||
|
||||
override fun bind(model: Model) {
|
||||
@@ -69,8 +74,9 @@ object RecipientPreference {
|
||||
|
||||
if (model.lifecycleOwner != null) {
|
||||
observeRecipient(model.lifecycleOwner, model.recipient)
|
||||
model.memberLabel?.let(::showMemberLabel)
|
||||
} else {
|
||||
onRecipientChanged(model.recipient)
|
||||
onRecipientChanged(model.recipient, model.memberLabel)
|
||||
}
|
||||
|
||||
admin?.visible = model.isAdmin
|
||||
@@ -80,7 +86,7 @@ object RecipientPreference {
|
||||
unbind()
|
||||
}
|
||||
|
||||
private fun onRecipientChanged(recipient: Recipient) {
|
||||
private fun onRecipientChanged(recipient: Recipient, memberLabel: StyledMemberLabel? = null) {
|
||||
avatar.setRecipient(recipient)
|
||||
badge.setBadgeFromRecipient(recipient)
|
||||
name.text = if (recipient.isSelf) {
|
||||
@@ -98,15 +104,36 @@ object RecipientPreference {
|
||||
}
|
||||
}
|
||||
|
||||
val aboutText = recipient.combinedAboutAndEmoji
|
||||
if (aboutText.isNullOrEmpty()) {
|
||||
about?.visibility = View.GONE
|
||||
} else {
|
||||
about?.text = recipient.combinedAboutAndEmoji
|
||||
about?.visibility = View.VISIBLE
|
||||
when {
|
||||
memberLabel != null -> showMemberLabel(memberLabel)
|
||||
|
||||
!recipient.combinedAboutAndEmoji.isNullOrEmpty() -> {
|
||||
about?.text = recipient.combinedAboutAndEmoji
|
||||
about?.visible = true
|
||||
memberLabelView?.visible = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
memberLabelView?.visible = false
|
||||
about?.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMemberLabel(styledLabel: StyledMemberLabel) {
|
||||
memberLabelView?.apply {
|
||||
style = MemberLabelPillView.Style(
|
||||
horizontalPadding = 8.dp,
|
||||
verticalPadding = 2.dp,
|
||||
textStyle = { MaterialTheme.typography.bodySmall }
|
||||
)
|
||||
setLabel(styledLabel.label, styledLabel.tintColor)
|
||||
visible = true
|
||||
}
|
||||
|
||||
about?.visible = false
|
||||
}
|
||||
|
||||
private fun observeRecipient(lifecycleOwner: LifecycleOwner?, recipient: Recipient?) {
|
||||
this.recipient?.live()?.liveData?.removeObserver(recipientObserver)
|
||||
|
||||
|
||||
@@ -1289,6 +1289,18 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all member labels in the group. Only includes members that have a non-blank label.
|
||||
*/
|
||||
fun memberLabelsByAci(): Map<ACI, MemberLabel> = buildMap {
|
||||
decryptedGroup.members.forEach { member ->
|
||||
if (member.labelString.isNotBlank()) {
|
||||
val aci = ACI.parseOrNull(member.aciBytes) ?: return@forEach
|
||||
put(aci, MemberLabel(member.labelEmoji, member.labelString))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(BadGroupIdException::class)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.groups.memberlabel
|
||||
|
||||
import androidx.annotation.ColorInt
|
||||
|
||||
/**
|
||||
* A member's custom label within a group.
|
||||
*/
|
||||
data class MemberLabel(
|
||||
val emoji: String?,
|
||||
val text: String
|
||||
)
|
||||
|
||||
data class StyledMemberLabel(
|
||||
val label: MemberLabel,
|
||||
@param:ColorInt val tintColor: Int
|
||||
)
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Previews
|
||||
@@ -66,7 +67,8 @@ fun MemberLabelPill(
|
||||
text = text,
|
||||
color = textColor,
|
||||
style = textStyle,
|
||||
maxLines = 1
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,18 @@ package org.thoughtcrime.securesms.groups.memberlabel
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.AbstractComposeView
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* @see MemberLabelPill
|
||||
@@ -26,6 +32,8 @@ class MemberLabelPillView : AbstractComposeView {
|
||||
private var memberLabel: MemberLabel? by mutableStateOf(null)
|
||||
private var tintColor: Color by mutableStateOf(Color.Unspecified)
|
||||
|
||||
var style: Style by mutableStateOf(Style())
|
||||
|
||||
fun setLabel(label: MemberLabel, tintColor: Color) {
|
||||
this.memberLabel = label
|
||||
this.tintColor = tintColor
|
||||
@@ -42,8 +50,16 @@ class MemberLabelPillView : AbstractComposeView {
|
||||
MemberLabelPill(
|
||||
emoji = label.emoji,
|
||||
text = label.text,
|
||||
tintColor = tintColor
|
||||
tintColor = tintColor,
|
||||
modifier = Modifier.padding(horizontal = style.horizontalPadding, vertical = style.verticalPadding),
|
||||
textStyle = style.textStyle()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class Style(
|
||||
val horizontalPadding: Dp = 12.dp,
|
||||
val verticalPadding: Dp = 2.dp,
|
||||
val textStyle: @Composable () -> TextStyle = { MaterialTheme.typography.bodyLarge }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -59,6 +59,27 @@ class MemberLabelRepository private constructor(
|
||||
return@withContext groupRecord.requireV2GroupProperties().memberLabel(aci)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets member labels for a list of recipients in a group.
|
||||
*
|
||||
* Returns a map of [RecipientId] to [MemberLabel] for members that have labels.
|
||||
*/
|
||||
suspend fun getLabels(groupId: GroupId.V2, recipients: List<Recipient>): Map<RecipientId, MemberLabel> = withContext(Dispatchers.IO) {
|
||||
if (!RemoteConfig.receiveMemberLabels) {
|
||||
return@withContext emptyMap()
|
||||
}
|
||||
|
||||
val groupRecord = groupsTable.getGroup(groupId).orNull() ?: return@withContext emptyMap()
|
||||
val labelsByAci = groupRecord.requireV2GroupProperties().memberLabelsByAci()
|
||||
|
||||
buildMap {
|
||||
recipients.forEach { recipient ->
|
||||
val aci = recipient.serviceId.orNull() as? ServiceId.ACI
|
||||
labelsByAci[aci]?.let { label -> put(recipient.id, label) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the group member label for the current user.
|
||||
*/
|
||||
@@ -70,11 +91,3 @@ class MemberLabelRepository private constructor(
|
||||
GroupManager.updateMemberLabel(context, groupId, label.text, label.emoji.orEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A member's custom label within a group.
|
||||
*/
|
||||
data class MemberLabel(
|
||||
val emoji: String?,
|
||||
val text: String
|
||||
)
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
package org.thoughtcrime.securesms.recipients.ui.bottomsheet
|
||||
|
||||
import androidx.annotation.ColorInt
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabel
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.StyledMemberLabel
|
||||
|
||||
data class RecipientDetailsState(
|
||||
val memberLabel: StyledMemberLabel?,
|
||||
val aboutText: String?
|
||||
)
|
||||
|
||||
data class StyledMemberLabel(
|
||||
val label: MemberLabel,
|
||||
@param:ColorInt val tintColor: Int
|
||||
)
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.LiveGroup;
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabel;
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelRepository;
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.StyledMemberLabel;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||
import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupsActivity;
|
||||
@@ -69,6 +70,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
private final MutableLiveData<RecipientDetailsState> recipientDetailsState;
|
||||
private final CompositeDisposable disposables;
|
||||
private final boolean isDeprecatedOrUnregistered;
|
||||
|
||||
private RecipientDialogViewModel(@NonNull Context context,
|
||||
@NonNull RecipientDialogRepository recipientDialogRepository)
|
||||
{
|
||||
@@ -198,8 +200,8 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
activity.startActivity(StoryViewerActivity.createIntent(
|
||||
activity,
|
||||
new StoryViewerArgs.Builder(recipientDialogRepository.getRecipientId(), recipient.getValue().getShouldHideStory())
|
||||
.isFromQuote(true)
|
||||
.build()));
|
||||
.isFromQuote(true)
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,65 +240,65 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
activity.startActivity(StoryViewerActivity.createIntent(
|
||||
activity,
|
||||
new StoryViewerArgs.Builder(recipientDialogRepository.getRecipientId(), recipient.getValue().getShouldHideStory())
|
||||
.isFromQuote(true)
|
||||
.build()));
|
||||
.isFromQuote(true)
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
void onMakeGroupAdminClicked(@NonNull Activity activity) {
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_s_will_be_able_to_edit_group, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_make_admin,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
recipientDialogRepository.setMemberAdmin(true, result -> {
|
||||
adminActionBusy.setValue(false);
|
||||
if (!result) {
|
||||
Toast.makeText(activity, R.string.ManageGroupActivity_failed_to_update_the_group, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
},
|
||||
this::showErrorToast);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {})
|
||||
.show();
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_s_will_be_able_to_edit_group, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_make_admin,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
recipientDialogRepository.setMemberAdmin(true, result -> {
|
||||
adminActionBusy.setValue(false);
|
||||
if (!result) {
|
||||
Toast.makeText(activity, R.string.ManageGroupActivity_failed_to_update_the_group, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
},
|
||||
this::showErrorToast);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {})
|
||||
.show();
|
||||
}
|
||||
|
||||
void onRemoveGroupAdminClicked(@NonNull Activity activity) {
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_remove_s_as_group_admin, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove_as_admin,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
recipientDialogRepository.setMemberAdmin(false, result -> {
|
||||
adminActionBusy.setValue(false);
|
||||
if (!result) {
|
||||
Toast.makeText(activity, R.string.ManageGroupActivity_failed_to_update_the_group, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
},
|
||||
this::showErrorToast);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {})
|
||||
.show();
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_remove_s_as_group_admin, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove_as_admin,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
recipientDialogRepository.setMemberAdmin(false, result -> {
|
||||
adminActionBusy.setValue(false);
|
||||
if (!result) {
|
||||
Toast.makeText(activity, R.string.ManageGroupActivity_failed_to_update_the_group, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
},
|
||||
this::showErrorToast);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {})
|
||||
.show();
|
||||
}
|
||||
|
||||
void onRemoveFromGroupClicked(@NonNull Activity activity, boolean isLinkActive, @NonNull Runnable onSuccess) {
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(isLinkActive ? R.string.RecipientBottomSheet_remove_s_from_the_group_they_will_not_be_able_to_rejoin
|
||||
: R.string.RecipientBottomSheet_remove_s_from_the_group,
|
||||
Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
recipientDialogRepository.removeMember(result -> {
|
||||
adminActionBusy.setValue(false);
|
||||
if (result) {
|
||||
onSuccess.run();
|
||||
}
|
||||
},
|
||||
this::showErrorToast);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {})
|
||||
.show();
|
||||
.setMessage(context.getString(isLinkActive ? R.string.RecipientBottomSheet_remove_s_from_the_group_they_will_not_be_able_to_rejoin
|
||||
: R.string.RecipientBottomSheet_remove_s_from_the_group,
|
||||
Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
recipientDialogRepository.removeMember(result -> {
|
||||
adminActionBusy.setValue(false);
|
||||
if (result) {
|
||||
onSuccess.run();
|
||||
}
|
||||
},
|
||||
this::showErrorToast);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {})
|
||||
.show();
|
||||
}
|
||||
|
||||
void refreshRecipient() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?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"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -9,7 +8,8 @@
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:minHeight="64dp"
|
||||
android:paddingHorizontal="@dimen/dsl_settings_gutter">
|
||||
android:paddingHorizontal="@dimen/dsl_settings_gutter"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
android:id="@+id/recipient_avatar"
|
||||
@@ -47,21 +47,43 @@
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/recipient_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:maxLines="2"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
app:layout_constraintBottom_toTopOf="@+id/recipient_about"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/recipient_member_label"
|
||||
app:layout_constraintEnd_toStartOf="@+id/admin"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toEndOf="@+id/recipient_avatar"
|
||||
app:layout_constraintTop_toTopOf="@+id/recipient_avatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Miles Morales" />
|
||||
|
||||
<org.thoughtcrime.securesms.groups.memberlabel.MemberLabelPillView
|
||||
android:id="@+id/recipient_member_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@+id/recipient_about"
|
||||
app:layout_constraintEnd_toStartOf="@+id/popupMenuProgressContainer"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="@+id/recipient_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/recipient_name"
|
||||
app:layout_constraintWidth_default="wrap"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/recipient_content_end_barrier"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:barrierDirection="end"
|
||||
app:constraint_referenced_ids="recipient_name,recipient_member_label, recipient_about" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/recipient_about"
|
||||
android:layout_width="0dp"
|
||||
@@ -74,17 +96,18 @@
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/recipient_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/admin"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toEndOf="@+id/recipient_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/recipient_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/recipient_member_label"
|
||||
tools:text="Hangin' around the web" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/admin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="@string/GroupRecipientListItem_admin"
|
||||
@@ -93,8 +116,8 @@
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/popupMenuProgressContainer"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toEndOf="@+id/recipient_name"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toEndOf="@+id/recipient_content_end_barrier"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_goneMarginEnd="0dp"
|
||||
tools:visibility="visible" />
|
||||
@@ -132,4 +155,4 @@
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
android:id="@+id/rbs_member_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="6dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
Reference in New Issue
Block a user