Enable sharing to stories and refactor share activity.

This commit is contained in:
Alex Hart
2022-04-07 15:47:56 -03:00
committed by Greyson Parrelli
parent fd4543ffe0
commit 523537cf05
62 changed files with 1188 additions and 1855 deletions

View File

@@ -12,6 +12,7 @@ import android.widget.TextView
import android.widget.Toast
import androidx.annotation.PluralsRes
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.setFragmentResultListener
@@ -79,11 +80,17 @@ class MultiselectForwardFragment :
private var handler: Handler? = null
private fun createViewModelFactory(): MultiselectForwardViewModel.Factory {
return MultiselectForwardViewModel.Factory(getMultiShareArgs(), MultiselectForwardRepository(requireContext()))
return MultiselectForwardViewModel.Factory(getMultiShareArgs(), isSelectionOnly, MultiselectForwardRepository(requireContext()))
}
private fun getMultiShareArgs(): ArrayList<MultiShareArgs> = requireNotNull(requireArguments().getParcelableArrayList(ARG_MULTISHARE_ARGS))
private val forceDisableAddMessage: Boolean
get() = requireArguments().getBoolean(ARG_FORCE_DISABLE_ADD_MESSAGE, false)
private val isSelectionOnly: Boolean
get() = requireArguments().getBoolean(ARG_FORCE_SELECTION_ONLY, false)
override fun onGetLayoutInflater(savedInstanceState: Bundle?): LayoutInflater {
return if (parentFragment != null) {
requireParentFragment().onGetLayoutInflater(savedInstanceState)
@@ -155,7 +162,7 @@ class MultiselectForwardFragment :
contactSearchMediator.getSelectionState().observe(viewLifecycleOwner) { contactSelection ->
shareSelectionAdapter.submitList(contactSelection.mapIndexed { index, key -> ShareSelectionMappingModel(key.requireShareContact(), index == 0) })
addMessage.visible = contactSelection.any { key -> key !is ContactSearchKey.Story } && getMultiShareArgs().isNotEmpty()
addMessage.visible = !forceDisableAddMessage && contactSelection.any { key -> key !is ContactSearchKey.RecipientSearchKey.Story } && getMultiShareArgs().isNotEmpty()
if (contactSelection.isNotEmpty() && !bottomBar.isVisible) {
bottomBar.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.slide_fade_from_bottom)
@@ -188,13 +195,13 @@ class MultiselectForwardFragment :
setFragmentResultListener(CreateStoryWithViewersFragment.REQUEST_KEY) { _, bundle ->
val recipientId: RecipientId = bundle.getParcelable(CreateStoryWithViewersFragment.STORY_RECIPIENT)!!
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.Story(recipientId)))
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey.Story(recipientId)))
contactFilterView.clear()
}
setFragmentResultListener(ChooseGroupStoryBottomSheet.GROUP_STORY) { _, bundle ->
val groups: Set<RecipientId> = bundle.getParcelableArrayList<RecipientId>(ChooseGroupStoryBottomSheet.RESULT_SET)?.toSet() ?: emptySet()
val keys: Set<ContactSearchKey.Story> = groups.map { ContactSearchKey.Story(it) }.toSet()
val keys: Set<ContactSearchKey.RecipientSearchKey.Story> = groups.map { ContactSearchKey.RecipientSearchKey.Story(it) }.toSet()
contactSearchMediator.addToVisibleGroupStories(keys)
contactSearchMediator.setKeysSelected(keys)
contactFilterView.clear()
@@ -392,6 +399,8 @@ class MultiselectForwardFragment :
const val ARG_MULTISHARE_ARGS = "multiselect.forward.fragment.arg.multishare.args"
const val ARG_CAN_SEND_TO_NON_PUSH = "multiselect.forward.fragment.arg.can.send.to.non.push"
const val ARG_TITLE = "multiselect.forward.fragment.title"
const val ARG_FORCE_DISABLE_ADD_MESSAGE = "multiselect.forward.fragment.force.disable.add.message"
const val ARG_FORCE_SELECTION_ONLY = "multiselect.forward.fragment.force.disable.add.message"
const val RESULT_KEY = "result_key"
const val RESULT_SELECTION = "result_selection_recipients"
const val RESULT_SENT = "result_sent"
@@ -400,26 +409,37 @@ class MultiselectForwardFragment :
fun showBottomSheet(supportFragmentManager: FragmentManager, multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs) {
val fragment = MultiselectForwardBottomSheet()
fragment.arguments = Bundle().apply {
putParcelableArrayList(ARG_MULTISHARE_ARGS, ArrayList(multiselectForwardFragmentArgs.multiShareArgs))
putBoolean(ARG_CAN_SEND_TO_NON_PUSH, multiselectForwardFragmentArgs.canSendToNonPush)
putInt(ARG_TITLE, multiselectForwardFragmentArgs.title)
}
fragment.show(supportFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
showDialogFragment(supportFragmentManager, fragment, multiselectForwardFragmentArgs)
}
@JvmStatic
fun showFullScreen(supportFragmentManager: FragmentManager, multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs) {
val fragment = MultiselectForwardFullScreenDialogFragment()
fragment.arguments = Bundle().apply {
putParcelableArrayList(ARG_MULTISHARE_ARGS, ArrayList(multiselectForwardFragmentArgs.multiShareArgs))
putBoolean(ARG_CAN_SEND_TO_NON_PUSH, multiselectForwardFragmentArgs.canSendToNonPush)
putInt(ARG_TITLE, multiselectForwardFragmentArgs.title)
showDialogFragment(supportFragmentManager, fragment, multiselectForwardFragmentArgs)
}
@JvmStatic
fun create(multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs): Fragment {
return MultiselectForwardFragment().apply {
arguments = createArgumentsBundle(multiselectForwardFragmentArgs)
}
}
private fun showDialogFragment(supportFragmentManager: FragmentManager, fragment: DialogFragment, multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs) {
fragment.arguments = createArgumentsBundle(multiselectForwardFragmentArgs)
fragment.show(supportFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
}
private fun createArgumentsBundle(multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs): Bundle {
return Bundle().apply {
putParcelableArrayList(ARG_MULTISHARE_ARGS, ArrayList(multiselectForwardFragmentArgs.multiShareArgs))
putBoolean(ARG_CAN_SEND_TO_NON_PUSH, multiselectForwardFragmentArgs.canSendToNonPush)
putInt(ARG_TITLE, multiselectForwardFragmentArgs.title)
putBoolean(ARG_FORCE_DISABLE_ADD_MESSAGE, multiselectForwardFragmentArgs.forceDisableAddMessage)
putBoolean(ARG_FORCE_SELECTION_ONLY, multiselectForwardFragmentArgs.forceSelectionOnly)
}
}
}
}

View File

@@ -23,14 +23,18 @@ import java.util.function.Consumer
/**
* Arguments for the MultiselectForwardFragment.
*
* @param canSendToNonPush Whether non-push recipients will be displayed
* @param multiShareArgs The items to forward. If this is an empty list, the fragment owner will be sent back a selected list of contacts.
* @param title The title to display at the top of the sheet
* @param canSendToNonPush Whether non-push recipients will be displayed
* @param multiShareArgs The items to forward. If this is an empty list, the fragment owner will be sent back a selected list of contacts.
* @param title The title to display at the top of the sheet
* @param forceDisableAddMessage Hide the add message field even if it would normally be available.
* @param forceSelectionOnly Force the fragment to only select recipients, never actually performing the send.
*/
class MultiselectForwardFragmentArgs(
class MultiselectForwardFragmentArgs @JvmOverloads constructor(
val canSendToNonPush: Boolean,
val multiShareArgs: List<MultiShareArgs> = listOf(),
@StringRes val title: Int = R.string.MultiselectForwardFragment__forward_to
@StringRes val title: Int = R.string.MultiselectForwardFragment__forward_to,
val forceDisableAddMessage: Boolean = false,
val forceSelectionOnly: Boolean = false
) {
companion object {

View File

@@ -5,12 +5,10 @@ import io.reactivex.rxjava3.core.Single
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sharing.MultiShareArgs
import org.thoughtcrime.securesms.sharing.MultiShareSender
import org.thoughtcrime.securesms.sharing.ShareContactAndThread
import java.util.Optional
class MultiselectForwardRepository(context: Context) {
@@ -44,28 +42,20 @@ class MultiselectForwardRepository(context: Context) {
resultHandlers: MultiselectForwardResultHandlers
) {
SignalExecutors.BOUNDED.execute {
val threadDatabase: ThreadDatabase = SignalDatabase.threads
val sharedContactsAndThreads: Set<ShareContactAndThread> = shareContacts
val filteredContacts: Set<ContactSearchKey> = shareContacts
.asSequence()
.filter { it is ContactSearchKey.Story || it is ContactSearchKey.KnownRecipient }
.map {
val recipient = Recipient.resolved(it.requireShareContact().recipientId.get())
val isStory = it is ContactSearchKey.Story || recipient.isDistributionList
val thread = if (isStory) -1L else threadDatabase.getOrCreateThreadIdFor(recipient)
ShareContactAndThread(recipient.id, thread, recipient.isForceSmsSelection, it is ContactSearchKey.Story)
}
.filter { it is ContactSearchKey.RecipientSearchKey.Story || it is ContactSearchKey.RecipientSearchKey.KnownRecipient }
.toSet()
val mappedArgs: List<MultiShareArgs> = multiShareArgs.map { it.buildUpon(sharedContactsAndThreads).build() }
val mappedArgs: List<MultiShareArgs> = multiShareArgs.map { it.buildUpon(filteredContacts).build() }
val results = mappedArgs.sortedBy { it.timestamp }.map { MultiShareSender.sendSync(it) }
if (additionalMessage.isNotEmpty()) {
val additional = MultiShareArgs.Builder(sharedContactsAndThreads.filterNot { it.isStory }.toSet())
val additional = MultiShareArgs.Builder(filteredContacts.filterNot { it is ContactSearchKey.RecipientSearchKey.Story }.toSet())
.withDraftText(additionalMessage)
.build()
if (additional.shareContactAndThreads.isNotEmpty()) {
if (additional.contactSearchKeys.isNotEmpty()) {
val additionalResult: MultiShareSender.MultiShareSendResultCollection = MultiShareSender.sendSync(additional)
handleResults(results + additionalResult, resultHandlers)

View File

@@ -4,7 +4,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.contacts.paged.RecipientSearchKey
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords
import org.thoughtcrime.securesms.sharing.MultiShareArgs
@@ -12,6 +11,7 @@ import org.thoughtcrime.securesms.util.livedata.Store
class MultiselectForwardViewModel(
private val records: List<MultiShareArgs>,
private val isSelectionOnly: Boolean,
private val repository: MultiselectForwardRepository
) : ViewModel() {
@@ -25,7 +25,7 @@ class MultiselectForwardViewModel(
store.update { it.copy(stage = MultiselectForwardState.Stage.FirstConfirmation) }
} else {
store.update { it.copy(stage = MultiselectForwardState.Stage.LoadingIdentities) }
UntrustedRecords.checkForBadIdentityRecords(selectedContacts.filterIsInstance(RecipientSearchKey::class.java).toSet()) { identityRecords ->
UntrustedRecords.checkForBadIdentityRecords(selectedContacts.filterIsInstance(ContactSearchKey.RecipientSearchKey::class.java).toSet()) { identityRecords ->
if (identityRecords.isEmpty()) {
performSend(additionalMessage, selectedContacts)
} else {
@@ -49,7 +49,7 @@ class MultiselectForwardViewModel(
private fun performSend(additionalMessage: String, selectedContacts: Set<ContactSearchKey>) {
store.update { it.copy(stage = MultiselectForwardState.Stage.SendPending) }
if (records.isEmpty()) {
if (records.isEmpty() || isSelectionOnly) {
store.update { it.copy(stage = MultiselectForwardState.Stage.SelectionConfirmed(selectedContacts)) }
} else {
repository.send(
@@ -67,10 +67,11 @@ class MultiselectForwardViewModel(
class Factory(
private val records: List<MultiShareArgs>,
private val repository: MultiselectForwardRepository
private val isSelectionOnly: Boolean,
private val repository: MultiselectForwardRepository,
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return requireNotNull(modelClass.cast(MultiselectForwardViewModel(records, repository)))
return requireNotNull(modelClass.cast(MultiselectForwardViewModel(records, isSelectionOnly, repository)))
}
}
}