kopia lustrzana https://github.com/ryukoposting/Signal-Android
876 wiersze
35 KiB
Kotlin
876 wiersze
35 KiB
Kotlin
package org.thoughtcrime.securesms.components.settings.conversation
|
|
|
|
import android.content.ActivityNotFoundException
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.graphics.PorterDuff
|
|
import android.graphics.PorterDuffColorFilter
|
|
import android.graphics.Rect
|
|
import android.os.Bundle
|
|
import android.view.MenuItem
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.widget.FrameLayout
|
|
import android.widget.TextView
|
|
import android.widget.Toast
|
|
import androidx.appcompat.widget.Toolbar
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.core.view.doOnPreDraw
|
|
import androidx.fragment.app.viewModels
|
|
import androidx.navigation.Navigation
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import app.cash.exhaustive.Exhaustive
|
|
import com.google.android.flexbox.FlexboxLayoutManager
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
import com.google.android.material.snackbar.Snackbar
|
|
import org.signal.core.util.DimensionUnit
|
|
import org.thoughtcrime.securesms.AvatarPreviewActivity
|
|
import org.thoughtcrime.securesms.BlockUnblockDialog
|
|
import org.thoughtcrime.securesms.InviteActivity
|
|
import org.thoughtcrime.securesms.MuteDialog
|
|
import org.thoughtcrime.securesms.PushContactSelectionActivity
|
|
import org.thoughtcrime.securesms.R
|
|
import org.thoughtcrime.securesms.badges.BadgeImageView
|
|
import org.thoughtcrime.securesms.badges.Badges
|
|
import org.thoughtcrime.securesms.badges.Badges.displayBadges
|
|
import org.thoughtcrime.securesms.badges.models.Badge
|
|
import org.thoughtcrime.securesms.badges.view.ViewBadgeBottomSheetDialogFragment
|
|
import org.thoughtcrime.securesms.components.AvatarImageView
|
|
import org.thoughtcrime.securesms.components.recyclerview.OnScrollAnimationHelper
|
|
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
|
import org.thoughtcrime.securesms.components.settings.NO_TINT
|
|
import org.thoughtcrime.securesms.components.settings.configure
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.AvatarPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.BioTextPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.ButtonStripPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.GroupDescriptionPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.InternalPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.LargeIconClickPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.LegacyGroupPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.RecipientPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.SharedMediaPreference
|
|
import org.thoughtcrime.securesms.components.settings.conversation.preferences.Utils.formatMutedUntil
|
|
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader
|
|
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
|
import org.thoughtcrime.securesms.groups.ParcelableGroupId
|
|
import org.thoughtcrime.securesms.groups.ui.GroupErrors
|
|
import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog
|
|
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog
|
|
import org.thoughtcrime.securesms.groups.ui.addmembers.AddMembersActivity
|
|
import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupsActivity
|
|
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.ManagePendingAndRequestingMembersActivity
|
|
import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupDescriptionDialog
|
|
import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupInviteSentDialog
|
|
import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupsLearnMoreBottomSheetDialogFragment
|
|
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInitiationBottomSheetDialogFragment
|
|
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity
|
|
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
|
|
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity
|
|
import org.thoughtcrime.securesms.recipients.Recipient
|
|
import org.thoughtcrime.securesms.recipients.RecipientExporter
|
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
|
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment
|
|
import org.thoughtcrime.securesms.stories.Stories
|
|
import org.thoughtcrime.securesms.stories.StoryViewerArgs
|
|
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
|
|
import org.thoughtcrime.securesms.stories.viewer.AddToGroupStoryDelegate
|
|
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
|
|
import org.thoughtcrime.securesms.util.CommunicationActions
|
|
import org.thoughtcrime.securesms.util.ContextUtil
|
|
import org.thoughtcrime.securesms.util.ExpirationUtil
|
|
import org.thoughtcrime.securesms.util.Material3OnScrollHelper
|
|
import org.thoughtcrime.securesms.util.ViewUtil
|
|
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
|
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
|
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog
|
|
import org.thoughtcrime.securesms.verify.VerifyIdentityActivity
|
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperActivity
|
|
|
|
private const val REQUEST_CODE_VIEW_CONTACT = 1
|
|
private const val REQUEST_CODE_ADD_CONTACT = 2
|
|
private const val REQUEST_CODE_ADD_MEMBERS_TO_GROUP = 3
|
|
private const val REQUEST_CODE_RETURN_FROM_MEDIA = 4
|
|
|
|
class ConversationSettingsFragment : DSLSettingsFragment(
|
|
layoutId = R.layout.conversation_settings_fragment,
|
|
menuId = R.menu.conversation_settings
|
|
) {
|
|
|
|
private val alertTint by lazy { ContextCompat.getColor(requireContext(), R.color.signal_alert_primary) }
|
|
private val blockIcon by lazy {
|
|
ContextUtil.requireDrawable(requireContext(), R.drawable.ic_block_tinted_24).apply {
|
|
colorFilter = PorterDuffColorFilter(alertTint, PorterDuff.Mode.SRC_IN)
|
|
}
|
|
}
|
|
|
|
private val unblockIcon by lazy {
|
|
ContextUtil.requireDrawable(requireContext(), R.drawable.ic_block_tinted_24)
|
|
}
|
|
|
|
private val leaveIcon by lazy {
|
|
ContextUtil.requireDrawable(requireContext(), R.drawable.ic_leave_tinted_24).apply {
|
|
colorFilter = PorterDuffColorFilter(alertTint, PorterDuff.Mode.SRC_IN)
|
|
}
|
|
}
|
|
|
|
private val viewModel by viewModels<ConversationSettingsViewModel>(
|
|
factoryProducer = {
|
|
val args = ConversationSettingsFragmentArgs.fromBundle(requireArguments())
|
|
val groupId = args.groupId as? ParcelableGroupId
|
|
|
|
ConversationSettingsViewModel.Factory(
|
|
recipientId = args.recipientId,
|
|
groupId = ParcelableGroupId.get(groupId),
|
|
repository = ConversationSettingsRepository(requireContext())
|
|
)
|
|
}
|
|
)
|
|
|
|
private lateinit var callback: Callback
|
|
|
|
private lateinit var toolbar: Toolbar
|
|
private lateinit var toolbarAvatarContainer: FrameLayout
|
|
private lateinit var toolbarAvatar: AvatarImageView
|
|
private lateinit var toolbarBadge: BadgeImageView
|
|
private lateinit var toolbarTitle: TextView
|
|
private lateinit var toolbarBackground: View
|
|
private lateinit var addToGroupStoryDelegate: AddToGroupStoryDelegate
|
|
|
|
private val navController get() = Navigation.findNavController(requireView())
|
|
|
|
override fun onAttach(context: Context) {
|
|
super.onAttach(context)
|
|
|
|
callback = context as Callback
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
toolbar = view.findViewById(R.id.toolbar)
|
|
toolbarAvatarContainer = view.findViewById(R.id.toolbar_avatar_container)
|
|
toolbarAvatar = view.findViewById(R.id.toolbar_avatar)
|
|
toolbarBadge = view.findViewById(R.id.toolbar_badge)
|
|
toolbarTitle = view.findViewById(R.id.toolbar_title)
|
|
toolbarBackground = view.findViewById(R.id.toolbar_background)
|
|
|
|
val args: ConversationSettingsFragmentArgs = ConversationSettingsFragmentArgs.fromBundle(requireArguments())
|
|
if (args.recipientId != null) {
|
|
layoutManagerProducer = Badges::createLayoutManagerForGridWithBadges
|
|
}
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
recyclerView?.addOnScrollListener(ConversationSettingsOnUserScrolledAnimationHelper(toolbarAvatarContainer, toolbarTitle, toolbarBackground))
|
|
}
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
when (requestCode) {
|
|
REQUEST_CODE_ADD_MEMBERS_TO_GROUP -> if (data != null) {
|
|
val selected: List<RecipientId> = requireNotNull(data.getParcelableArrayListExtra(PushContactSelectionActivity.KEY_SELECTED_RECIPIENTS))
|
|
val progress: SimpleProgressDialog.DismissibleDialog = SimpleProgressDialog.showDelayed(requireContext())
|
|
|
|
viewModel.onAddToGroupComplete(selected) {
|
|
progress.dismiss()
|
|
}
|
|
}
|
|
REQUEST_CODE_RETURN_FROM_MEDIA -> viewModel.refreshSharedMedia()
|
|
REQUEST_CODE_ADD_CONTACT -> viewModel.refreshRecipient()
|
|
REQUEST_CODE_VIEW_CONTACT -> viewModel.refreshRecipient()
|
|
}
|
|
}
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
return if (item.itemId == R.id.action_edit) {
|
|
val args = ConversationSettingsFragmentArgs.fromBundle(requireArguments())
|
|
val groupId = args.groupId as ParcelableGroupId
|
|
|
|
startActivity(EditProfileActivity.getIntentForGroupProfile(requireActivity(), requireNotNull(ParcelableGroupId.get(groupId))))
|
|
true
|
|
} else {
|
|
super.onOptionsItemSelected(item)
|
|
}
|
|
}
|
|
|
|
override fun getMaterial3OnScrollHelper(toolbar: Toolbar?): Material3OnScrollHelper {
|
|
return object : Material3OnScrollHelper(requireActivity(), toolbar!!) {
|
|
override val inactiveColorSet = ColorSet(
|
|
toolbarColorRes = R.color.signal_colorBackground_0,
|
|
statusBarColorRes = R.color.signal_colorBackground
|
|
)
|
|
}
|
|
}
|
|
|
|
override fun bindAdapter(adapter: MappingAdapter) {
|
|
val args = ConversationSettingsFragmentArgs.fromBundle(requireArguments())
|
|
|
|
BioTextPreference.register(adapter)
|
|
AvatarPreference.register(adapter)
|
|
ButtonStripPreference.register(adapter)
|
|
LargeIconClickPreference.register(adapter)
|
|
SharedMediaPreference.register(adapter)
|
|
RecipientPreference.register(adapter)
|
|
InternalPreference.register(adapter)
|
|
GroupDescriptionPreference.register(adapter)
|
|
LegacyGroupPreference.register(adapter)
|
|
|
|
val recipientId = args.recipientId
|
|
if (recipientId != null) {
|
|
Badge.register(adapter) { badge, _, _ ->
|
|
ViewBadgeBottomSheetDialogFragment.show(parentFragmentManager, recipientId, badge)
|
|
}
|
|
}
|
|
|
|
addToGroupStoryDelegate = AddToGroupStoryDelegate(this)
|
|
viewModel.state.observe(viewLifecycleOwner) { state ->
|
|
|
|
if (state.recipient != Recipient.UNKNOWN) {
|
|
toolbarAvatar.buildOptions()
|
|
.withQuickContactEnabled(false)
|
|
.withUseSelfProfileAvatar(false)
|
|
.withFixedSize(ViewUtil.dpToPx(80))
|
|
.load(state.recipient)
|
|
|
|
if (!state.recipient.isSelf) {
|
|
toolbarBadge.setBadgeFromRecipient(state.recipient)
|
|
}
|
|
|
|
state.withRecipientSettingsState {
|
|
toolbarTitle.text = if (state.recipient.isSelf) getString(R.string.note_to_self) else state.recipient.getDisplayName(requireContext())
|
|
}
|
|
|
|
state.withGroupSettingsState {
|
|
toolbarTitle.text = it.groupTitle
|
|
toolbar.menu.findItem(R.id.action_edit).isVisible = it.canEditGroupAttributes
|
|
}
|
|
}
|
|
|
|
adapter.submitList(getConfiguration(state).toMappingModelList()) {
|
|
if (state.isLoaded) {
|
|
(view?.parent as? ViewGroup)?.doOnPreDraw {
|
|
callback.onContentWillRender()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
viewModel.events.observe(viewLifecycleOwner) { event ->
|
|
@Exhaustive
|
|
when (event) {
|
|
is ConversationSettingsEvent.AddToAGroup -> handleAddToAGroup(event)
|
|
is ConversationSettingsEvent.AddMembersToGroup -> handleAddMembersToGroup(event)
|
|
ConversationSettingsEvent.ShowGroupHardLimitDialog -> showGroupHardLimitDialog()
|
|
is ConversationSettingsEvent.ShowAddMembersToGroupError -> showAddMembersToGroupError(event)
|
|
is ConversationSettingsEvent.ShowGroupInvitesSentDialog -> showGroupInvitesSentDialog(event)
|
|
is ConversationSettingsEvent.ShowMembersAdded -> showMembersAdded(event)
|
|
is ConversationSettingsEvent.InitiateGroupMigration -> GroupsV1MigrationInitiationBottomSheetDialogFragment.showForInitiation(parentFragmentManager, event.recipientId)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getConfiguration(state: ConversationSettingsState): DSLConfiguration {
|
|
return configure {
|
|
if (state.recipient == Recipient.UNKNOWN) {
|
|
return@configure
|
|
}
|
|
|
|
customPref(
|
|
AvatarPreference.Model(
|
|
recipient = state.recipient,
|
|
storyViewState = state.storyViewState,
|
|
onAvatarClick = { avatar ->
|
|
val viewAvatarIntent = AvatarPreviewActivity.intentFromRecipientId(requireContext(), state.recipient.id)
|
|
val viewAvatarTransitionBundle = AvatarPreviewActivity.createTransitionBundle(requireActivity(), avatar)
|
|
|
|
if (Stories.isFeatureEnabled() && avatar.hasStory()) {
|
|
val viewStoryIntent = StoryViewerActivity.createIntent(
|
|
requireContext(),
|
|
StoryViewerArgs(
|
|
recipientId = state.recipient.id,
|
|
isInHiddenStoryMode = state.recipient.shouldHideStory(),
|
|
isFromQuote = true
|
|
)
|
|
)
|
|
StoryDialogs.displayStoryOrProfileImage(
|
|
context = requireContext(),
|
|
onViewStory = { startActivity(viewStoryIntent) },
|
|
onViewAvatar = { startActivity(viewAvatarIntent, viewAvatarTransitionBundle) }
|
|
)
|
|
} else if (!state.recipient.isSelf) {
|
|
startActivity(viewAvatarIntent, viewAvatarTransitionBundle)
|
|
}
|
|
},
|
|
onBadgeClick = { badge ->
|
|
ViewBadgeBottomSheetDialogFragment.show(parentFragmentManager, state.recipient.id, badge)
|
|
}
|
|
)
|
|
)
|
|
|
|
state.withRecipientSettingsState {
|
|
customPref(BioTextPreference.RecipientModel(recipient = state.recipient))
|
|
}
|
|
|
|
state.withGroupSettingsState { groupState ->
|
|
|
|
val groupMembershipDescription = if (groupState.groupId.isV1) {
|
|
String.format("%s · %s", groupState.membershipCountDescription, getString(R.string.ManageGroupActivity_legacy_group))
|
|
} else if (!groupState.canEditGroupAttributes && groupState.groupDescription.isNullOrEmpty()) {
|
|
groupState.membershipCountDescription
|
|
} else {
|
|
null
|
|
}
|
|
|
|
customPref(
|
|
BioTextPreference.GroupModel(
|
|
groupTitle = groupState.groupTitle,
|
|
groupMembershipDescription = groupMembershipDescription
|
|
)
|
|
)
|
|
|
|
if (groupState.groupId.isV2) {
|
|
customPref(
|
|
GroupDescriptionPreference.Model(
|
|
groupId = groupState.groupId,
|
|
groupDescription = groupState.groupDescription,
|
|
descriptionShouldLinkify = groupState.groupDescriptionShouldLinkify,
|
|
canEditGroupAttributes = groupState.canEditGroupAttributes,
|
|
onEditGroupDescription = {
|
|
startActivity(EditProfileActivity.getIntentForGroupProfile(requireActivity(), groupState.groupId))
|
|
},
|
|
onViewGroupDescription = {
|
|
GroupDescriptionDialog.show(childFragmentManager, groupState.groupId, null, groupState.groupDescriptionShouldLinkify)
|
|
}
|
|
)
|
|
)
|
|
} else if (groupState.legacyGroupState != LegacyGroupPreference.State.NONE) {
|
|
customPref(
|
|
LegacyGroupPreference.Model(
|
|
state = groupState.legacyGroupState,
|
|
onLearnMoreClick = { GroupsLearnMoreBottomSheetDialogFragment.show(parentFragmentManager) },
|
|
onUpgradeClick = { viewModel.initiateGroupUpgrade() },
|
|
onMmsWarningClick = { startActivity(Intent(requireContext(), InviteActivity::class.java)) }
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
if (state.displayInternalRecipientDetails) {
|
|
customPref(
|
|
InternalPreference.Model(
|
|
recipient = state.recipient,
|
|
onInternalDetailsClicked = {
|
|
val action = ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToInternalDetailsSettingsFragment(state.recipient.id)
|
|
navController.safeNavigate(action)
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
customPref(
|
|
ButtonStripPreference.Model(
|
|
state = state.buttonStripState,
|
|
onAddToStoryClick = {
|
|
if (state.recipient.isPushV2Group && state.requireGroupSettingsState().isAnnouncementGroup && !state.requireGroupSettingsState().isSelfAdmin) {
|
|
MaterialAlertDialogBuilder(requireContext())
|
|
.setTitle(R.string.ConversationSettingsFragment__cant_add_to_group_story)
|
|
.setMessage(R.string.ConversationSettingsFragment__only_admins_of_this_group_can_add_to_its_story)
|
|
.setPositiveButton(android.R.string.ok) { d, _ -> d.dismiss() }
|
|
.show()
|
|
} else {
|
|
addToGroupStoryDelegate.addToStory(state.recipient.id)
|
|
}
|
|
},
|
|
onVideoClick = {
|
|
if (state.recipient.isPushV2Group && state.requireGroupSettingsState().isAnnouncementGroup && !state.requireGroupSettingsState().isSelfAdmin) {
|
|
MaterialAlertDialogBuilder(requireContext())
|
|
.setTitle(R.string.ConversationActivity_cant_start_group_call)
|
|
.setMessage(R.string.ConversationActivity_only_admins_of_this_group_can_start_a_call)
|
|
.setPositiveButton(android.R.string.ok) { d, _ -> d.dismiss() }
|
|
.show()
|
|
} else {
|
|
CommunicationActions.startVideoCall(requireActivity(), state.recipient)
|
|
}
|
|
},
|
|
onAudioClick = {
|
|
CommunicationActions.startVoiceCall(requireActivity(), state.recipient)
|
|
},
|
|
onMuteClick = {
|
|
if (!state.buttonStripState.isMuted) {
|
|
MuteDialog.show(requireContext(), viewModel::setMuteUntil)
|
|
} else {
|
|
MaterialAlertDialogBuilder(requireContext())
|
|
.setMessage(state.recipient.muteUntil.formatMutedUntil(requireContext()))
|
|
.setPositiveButton(R.string.ConversationSettingsFragment__unmute) { dialog, _ ->
|
|
viewModel.unmute()
|
|
dialog.dismiss()
|
|
}
|
|
.setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
|
.show()
|
|
}
|
|
},
|
|
onSearchClick = {
|
|
val intent = ConversationIntents.createBuilder(requireContext(), state.recipient.id, state.threadId)
|
|
.withSearchOpen(true)
|
|
.build()
|
|
|
|
startActivity(intent)
|
|
requireActivity().finish()
|
|
}
|
|
)
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
val summary = DSLSettingsText.from(formatDisappearingMessagesLifespan(state.disappearingMessagesLifespan))
|
|
val icon = if (state.disappearingMessagesLifespan <= 0 || state.recipient.isBlocked) {
|
|
R.drawable.ic_update_timer_disabled_16
|
|
} else {
|
|
R.drawable.ic_update_timer_16
|
|
}
|
|
|
|
var enabled = !state.recipient.isBlocked
|
|
state.withGroupSettingsState {
|
|
enabled = it.canEditGroupAttributes && !state.recipient.isBlocked
|
|
}
|
|
|
|
if (!state.recipient.isReleaseNotes && !state.recipient.isBlocked) {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__disappearing_messages),
|
|
summary = summary,
|
|
icon = DSLSettingsIcon.from(icon),
|
|
isEnabled = enabled,
|
|
onClick = {
|
|
val action = ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToAppSettingsExpireTimer()
|
|
.setInitialValue(state.disappearingMessagesLifespan)
|
|
.setRecipientId(state.recipient.id)
|
|
.setForResultMode(false)
|
|
|
|
navController.safeNavigate(action)
|
|
}
|
|
)
|
|
}
|
|
|
|
if (!state.recipient.isReleaseNotes) {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.preferences__chat_color_and_wallpaper),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_color_24),
|
|
onClick = {
|
|
startActivity(ChatWallpaperActivity.createIntent(requireContext(), state.recipient.id))
|
|
}
|
|
)
|
|
}
|
|
|
|
if (!state.recipient.isSelf) {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__sounds_and_notifications),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_speaker_24),
|
|
onClick = {
|
|
val action = ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToSoundsAndNotificationsSettingsFragment(state.recipient.id)
|
|
|
|
navController.safeNavigate(action)
|
|
}
|
|
)
|
|
}
|
|
|
|
state.withRecipientSettingsState { recipientState ->
|
|
when (recipientState.contactLinkState) {
|
|
ContactLinkState.OPEN -> {
|
|
@Suppress("DEPRECATION")
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__contact_details),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_profile_circle_24),
|
|
onClick = {
|
|
startActivityForResult(Intent(Intent.ACTION_VIEW, state.recipient.contactUri), REQUEST_CODE_VIEW_CONTACT)
|
|
}
|
|
)
|
|
}
|
|
ContactLinkState.ADD -> {
|
|
@Suppress("DEPRECATION")
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__add_as_a_contact),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_plus_24),
|
|
onClick = {
|
|
try {
|
|
startActivityForResult(RecipientExporter.export(state.recipient).asAddContactIntent(), REQUEST_CODE_ADD_CONTACT)
|
|
} catch (e: ActivityNotFoundException) {
|
|
Toast.makeText(context, R.string.ConversationSettingsFragment__contacts_app_not_found, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
)
|
|
}
|
|
ContactLinkState.NONE -> {
|
|
}
|
|
}
|
|
|
|
if (recipientState.identityRecord != null) {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__view_safety_number),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_safety_number_24),
|
|
onClick = {
|
|
startActivity(VerifyIdentityActivity.newIntent(requireActivity(), recipientState.identityRecord))
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
if (state.sharedMedia != null && state.sharedMedia.count > 0) {
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(R.string.recipient_preference_activity__shared_media)
|
|
|
|
@Suppress("DEPRECATION")
|
|
customPref(
|
|
SharedMediaPreference.Model(
|
|
mediaCursor = state.sharedMedia,
|
|
mediaIds = state.sharedMediaIds,
|
|
onMediaRecordClick = { mediaRecord, isLtr ->
|
|
startActivityForResult(
|
|
MediaIntentFactory.intentFromMediaRecord(requireContext(), mediaRecord, isLtr, allMediaInRail = true),
|
|
REQUEST_CODE_RETURN_FROM_MEDIA
|
|
)
|
|
}
|
|
)
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__see_all),
|
|
onClick = {
|
|
startActivity(MediaOverviewActivity.forThread(requireContext(), state.threadId))
|
|
}
|
|
)
|
|
}
|
|
|
|
state.withRecipientSettingsState { recipientSettingsState ->
|
|
if (state.recipient.badges.isNotEmpty()) {
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(R.string.ManageProfileFragment_badges)
|
|
|
|
displayBadges(requireContext(), state.recipient.badges)
|
|
|
|
textPref(
|
|
summary = DSLSettingsText.from(
|
|
R.string.ConversationSettingsFragment__get_badges
|
|
)
|
|
)
|
|
}
|
|
|
|
if (recipientSettingsState.selfHasGroups && !state.recipient.isReleaseNotes) {
|
|
|
|
dividerPref()
|
|
|
|
val groupsInCommonCount = recipientSettingsState.allGroupsInCommon.size
|
|
sectionHeaderPref(
|
|
DSLSettingsText.from(
|
|
if (groupsInCommonCount == 0) {
|
|
getString(R.string.ManageRecipientActivity_no_groups_in_common)
|
|
} else {
|
|
resources.getQuantityString(
|
|
R.plurals.ManageRecipientActivity_d_groups_in_common,
|
|
groupsInCommonCount,
|
|
groupsInCommonCount
|
|
)
|
|
}
|
|
)
|
|
)
|
|
|
|
if (!state.recipient.isBlocked) {
|
|
customPref(
|
|
LargeIconClickPreference.Model(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__add_to_a_group),
|
|
icon = DSLSettingsIcon.from(R.drawable.add_to_a_group, NO_TINT),
|
|
onClick = {
|
|
viewModel.onAddToGroup()
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
for (group in recipientSettingsState.groupsInCommon) {
|
|
customPref(
|
|
RecipientPreference.Model(
|
|
recipient = group,
|
|
onClick = {
|
|
CommunicationActions.startConversation(requireActivity(), group, null)
|
|
requireActivity().finish()
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
if (recipientSettingsState.canShowMoreGroupsInCommon) {
|
|
customPref(
|
|
LargeIconClickPreference.Model(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__see_all),
|
|
icon = DSLSettingsIcon.from(R.drawable.show_more, NO_TINT),
|
|
onClick = {
|
|
viewModel.revealAllMembers()
|
|
}
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
state.withGroupSettingsState { groupState ->
|
|
val memberCount = groupState.allMembers.size
|
|
|
|
if (groupState.canAddToGroup || memberCount > 0) {
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from(resources.getQuantityString(R.plurals.ContactSelectionListFragment_d_members, memberCount, memberCount)))
|
|
}
|
|
|
|
if (groupState.canAddToGroup) {
|
|
customPref(
|
|
LargeIconClickPreference.Model(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__add_members),
|
|
icon = DSLSettingsIcon.from(R.drawable.add_to_a_group, NO_TINT),
|
|
onClick = {
|
|
viewModel.onAddToGroup()
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
for (member in groupState.members) {
|
|
customPref(
|
|
RecipientPreference.Model(
|
|
recipient = member.member,
|
|
isAdmin = member.isAdmin,
|
|
onClick = {
|
|
RecipientBottomSheetDialogFragment.create(member.member.id, groupState.groupId).show(parentFragmentManager, "BOTTOM")
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
if (groupState.canShowMoreGroupMembers) {
|
|
customPref(
|
|
LargeIconClickPreference.Model(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__see_all),
|
|
icon = DSLSettingsIcon.from(R.drawable.show_more, NO_TINT),
|
|
onClick = {
|
|
viewModel.revealAllMembers()
|
|
}
|
|
)
|
|
)
|
|
}
|
|
|
|
if (state.recipient.isPushV2Group) {
|
|
dividerPref()
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__group_link),
|
|
summary = DSLSettingsText.from(if (groupState.groupLinkEnabled) R.string.preferences_on else R.string.preferences_off),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_link_16),
|
|
onClick = {
|
|
navController.safeNavigate(ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToShareableGroupLinkFragment(groupState.groupId.requireV2().toString()))
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__requests_and_invites),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_update_group_add_16),
|
|
onClick = {
|
|
startActivity(ManagePendingAndRequestingMembersActivity.newIntent(requireContext(), groupState.groupId.requireV2()))
|
|
}
|
|
)
|
|
|
|
if (groupState.isSelfAdmin) {
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.ConversationSettingsFragment__permissions),
|
|
icon = DSLSettingsIcon.from(R.drawable.ic_lock_24),
|
|
onClick = {
|
|
val action = ConversationSettingsFragmentDirections.actionConversationSettingsFragmentToPermissionsSettingsFragment(ParcelableGroupId.from(groupState.groupId))
|
|
navController.safeNavigate(action)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
if (groupState.canLeave) {
|
|
dividerPref()
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from(R.string.conversation__menu_leave_group, alertTint),
|
|
icon = DSLSettingsIcon.from(leaveIcon),
|
|
onClick = {
|
|
LeaveGroupDialog.handleLeavePushGroup(requireActivity(), groupState.groupId.requirePush(), null)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
if (state.canModifyBlockedState) {
|
|
state.withRecipientSettingsState {
|
|
dividerPref()
|
|
}
|
|
|
|
state.withGroupSettingsState {
|
|
if (!it.canLeave) {
|
|
dividerPref()
|
|
}
|
|
}
|
|
|
|
val isBlocked = state.recipient.isBlocked
|
|
val isGroup = state.recipient.isPushGroup
|
|
|
|
val title = when {
|
|
isBlocked && isGroup -> R.string.ConversationSettingsFragment__unblock_group
|
|
isBlocked -> R.string.ConversationSettingsFragment__unblock
|
|
isGroup -> R.string.ConversationSettingsFragment__block_group
|
|
else -> R.string.ConversationSettingsFragment__block
|
|
}
|
|
|
|
val titleTint = if (isBlocked) null else alertTint
|
|
val blockUnblockIcon = if (isBlocked) unblockIcon else blockIcon
|
|
|
|
clickPref(
|
|
title = if (titleTint != null) DSLSettingsText.from(title, titleTint) else DSLSettingsText.from(title),
|
|
icon = DSLSettingsIcon.from(blockUnblockIcon),
|
|
onClick = {
|
|
if (state.recipient.isBlocked) {
|
|
BlockUnblockDialog.showUnblockFor(requireContext(), viewLifecycleOwner.lifecycle, state.recipient) {
|
|
viewModel.unblock()
|
|
}
|
|
} else {
|
|
BlockUnblockDialog.showBlockFor(requireContext(), viewLifecycleOwner.lifecycle, state.recipient) {
|
|
viewModel.block()
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun formatDisappearingMessagesLifespan(disappearingMessagesLifespan: Int): String {
|
|
return if (disappearingMessagesLifespan <= 0) {
|
|
getString(R.string.preferences_off)
|
|
} else {
|
|
ExpirationUtil.getExpirationDisplayValue(requireContext(), disappearingMessagesLifespan)
|
|
}
|
|
}
|
|
|
|
private fun handleAddToAGroup(addToAGroup: ConversationSettingsEvent.AddToAGroup) {
|
|
startActivity(AddToGroupsActivity.newIntent(requireContext(), addToAGroup.recipientId, addToAGroup.groupMembership))
|
|
}
|
|
|
|
@Suppress("DEPRECATION")
|
|
private fun handleAddMembersToGroup(addMembersToGroup: ConversationSettingsEvent.AddMembersToGroup) {
|
|
startActivityForResult(
|
|
AddMembersActivity.createIntent(
|
|
requireContext(),
|
|
addMembersToGroup.groupId,
|
|
ContactsCursorLoader.DisplayMode.FLAG_PUSH,
|
|
addMembersToGroup.selectionWarning,
|
|
addMembersToGroup.selectionLimit,
|
|
addMembersToGroup.isAnnouncementGroup,
|
|
addMembersToGroup.groupMembersWithoutSelf
|
|
),
|
|
REQUEST_CODE_ADD_MEMBERS_TO_GROUP
|
|
)
|
|
}
|
|
|
|
private fun showGroupHardLimitDialog() {
|
|
GroupLimitDialog.showHardLimitMessage(requireContext())
|
|
}
|
|
|
|
private fun showAddMembersToGroupError(showAddMembersToGroupError: ConversationSettingsEvent.ShowAddMembersToGroupError) {
|
|
Toast.makeText(requireContext(), GroupErrors.getUserDisplayMessage(showAddMembersToGroupError.failureReason), Toast.LENGTH_LONG).show()
|
|
}
|
|
|
|
private fun showGroupInvitesSentDialog(showGroupInvitesSentDialog: ConversationSettingsEvent.ShowGroupInvitesSentDialog) {
|
|
GroupInviteSentDialog.showInvitesSent(requireContext(), viewLifecycleOwner, showGroupInvitesSentDialog.invitesSentTo)
|
|
}
|
|
|
|
private fun showMembersAdded(showMembersAdded: ConversationSettingsEvent.ShowMembersAdded) {
|
|
val string = resources.getQuantityString(
|
|
R.plurals.ManageGroupActivity_added,
|
|
showMembersAdded.membersAddedCount,
|
|
showMembersAdded.membersAddedCount
|
|
)
|
|
|
|
Snackbar.make(requireView(), string, Snackbar.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private class ConversationSettingsOnUserScrolledAnimationHelper(
|
|
private val toolbarAvatar: View,
|
|
private val toolbarTitle: View,
|
|
private val toolbarBackground: View
|
|
) : OnScrollAnimationHelper() {
|
|
|
|
override val duration: Long = 200L
|
|
|
|
private val actionBarSize = DimensionUnit.DP.toPixels(64f)
|
|
private val rect = Rect()
|
|
|
|
override fun getAnimationState(recyclerView: RecyclerView): AnimationState {
|
|
val layoutManager = recyclerView.layoutManager!!
|
|
val firstVisibleItemPosition = if (layoutManager is FlexboxLayoutManager) {
|
|
layoutManager.findFirstVisibleItemPosition()
|
|
} else {
|
|
(layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
|
}
|
|
|
|
return if (firstVisibleItemPosition == 0) {
|
|
val firstChild = requireNotNull(layoutManager.getChildAt(0))
|
|
firstChild.getLocalVisibleRect(rect)
|
|
|
|
if (rect.height() <= actionBarSize) {
|
|
AnimationState.SHOW
|
|
} else {
|
|
AnimationState.HIDE
|
|
}
|
|
} else {
|
|
AnimationState.SHOW
|
|
}
|
|
}
|
|
|
|
override fun show(duration: Long) {
|
|
toolbarAvatar
|
|
.animate()
|
|
.setDuration(duration)
|
|
.translationY(0f)
|
|
.alpha(1f)
|
|
|
|
toolbarTitle
|
|
.animate()
|
|
.setDuration(duration)
|
|
.translationY(0f)
|
|
.alpha(1f)
|
|
|
|
toolbarBackground
|
|
.animate()
|
|
.setDuration(duration)
|
|
.alpha(1f)
|
|
}
|
|
|
|
override fun hide(duration: Long) {
|
|
toolbarAvatar
|
|
.animate()
|
|
.setDuration(duration)
|
|
.translationY(ViewUtil.dpToPx(56).toFloat())
|
|
.alpha(0f)
|
|
|
|
toolbarTitle
|
|
.animate()
|
|
.setDuration(duration)
|
|
.translationY(ViewUtil.dpToPx(56).toFloat())
|
|
.alpha(0f)
|
|
|
|
toolbarBackground
|
|
.animate()
|
|
.setDuration(duration)
|
|
.alpha(0f)
|
|
}
|
|
}
|
|
|
|
interface Callback {
|
|
fun onContentWillRender()
|
|
}
|
|
}
|