mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-24 10:51:27 +01:00
Implement video length enforcement for Stories.
This commit is contained in:
committed by
Cody Henthorne
parent
2c3d8337c3
commit
6a385c7a22
@@ -161,6 +161,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||
import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity;
|
||||
import org.thoughtcrime.securesms.stories.Stories;
|
||||
import org.thoughtcrime.securesms.stories.StoryViewerArgs;
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity;
|
||||
import org.thoughtcrime.securesms.util.CachedInflater;
|
||||
@@ -1422,8 +1423,8 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSendMediaToStories() {
|
||||
return true;
|
||||
public @Nullable Stories.MediaTransform.SendRequirements getStorySendRequirements() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||
|
||||
class MultiselectForwardBottomSheet : FixedRoundedCornerBottomSheetDialogFragment(), MultiselectForwardFragment.Callback {
|
||||
@@ -43,10 +44,9 @@ class MultiselectForwardBottomSheet : FixedRoundedCornerBottomSheetDialogFragmen
|
||||
return backgroundColor
|
||||
}
|
||||
|
||||
override fun canSendMediaToStories(): Boolean {
|
||||
return findListener<Callback>()?.canSendMediaToStories() ?: true
|
||||
override fun getStorySendRequirements(): Stories.MediaTransform.SendRequirements? {
|
||||
return findListener<Callback>()?.getStorySendRequirements()
|
||||
}
|
||||
|
||||
override fun setResult(bundle: Bundle) {
|
||||
setFragmentResult(MultiselectForwardFragment.RESULT_KEY, bundle)
|
||||
}
|
||||
@@ -71,6 +71,6 @@ class MultiselectForwardBottomSheet : FixedRoundedCornerBottomSheetDialogFragmen
|
||||
interface Callback {
|
||||
fun onFinishForwardAction()
|
||||
fun onDismissForwardSheet()
|
||||
fun canSendMediaToStories(): Boolean = true
|
||||
fun getStorySendRequirements(): Stories.MediaTransform.SendRequirements? = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.isVisible
|
||||
@@ -26,6 +28,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.ContactFilterView
|
||||
import org.thoughtcrime.securesms.components.TooltipPopup
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator
|
||||
@@ -80,6 +83,7 @@ class MultiselectForwardFragment :
|
||||
private lateinit var contactFilterView: ContactFilterView
|
||||
private lateinit var addMessage: EditText
|
||||
private lateinit var contactSearchMediator: ContactSearchMediator
|
||||
private lateinit var contactSearchRecycler: RecyclerView
|
||||
|
||||
private lateinit var callback: Callback
|
||||
private var dismissibleDialog: SimpleProgressDialog.DismissibleDialog? = null
|
||||
@@ -111,8 +115,8 @@ class MultiselectForwardFragment :
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
view.minimumHeight = resources.displayMetrics.heightPixels
|
||||
|
||||
val contactSearchRecycler: RecyclerView = view.findViewById(R.id.contact_selection_list)
|
||||
contactSearchMediator = ContactSearchMediator(this, contactSearchRecycler, FeatureFlags.shareSelectionLimit(), !isSingleRecipientSelection(), this::getConfiguration)
|
||||
contactSearchRecycler = view.findViewById(R.id.contact_selection_list)
|
||||
contactSearchMediator = ContactSearchMediator(this, contactSearchRecycler, FeatureFlags.shareSelectionLimit(), !isSingleRecipientSelection(), this::getConfiguration, this::filterContacts)
|
||||
|
||||
callback = findListener()!!
|
||||
disposables.bindTo(viewLifecycleOwner.lifecycle)
|
||||
@@ -356,6 +360,51 @@ class MultiselectForwardFragment :
|
||||
viewModel.cancelSend()
|
||||
}
|
||||
|
||||
private fun getStorySendRequirements(): Stories.MediaTransform.SendRequirements {
|
||||
return requireListener<Callback>().getStorySendRequirements() ?: viewModel.snapshot.storySendRequirements
|
||||
}
|
||||
|
||||
private fun filterContacts(view: View?, contactSet: Set<ContactSearchKey>): Set<ContactSearchKey> {
|
||||
val storySendRequirements = getStorySendRequirements()
|
||||
val resultsSet = contactSet.filterNot {
|
||||
it is ContactSearchKey.RecipientSearchKey && it.isStory && storySendRequirements == Stories.MediaTransform.SendRequirements.CAN_NOT_SEND
|
||||
}
|
||||
|
||||
if (view != null && contactSet.any { it is ContactSearchKey.RecipientSearchKey && it.isStory }) {
|
||||
@Suppress("NON_EXHAUSTIVE_WHEN_STATEMENT")
|
||||
when (storySendRequirements) {
|
||||
Stories.MediaTransform.SendRequirements.REQUIRES_CLIP -> {
|
||||
if (!SignalStore.storyValues().videoTooltipSeen) {
|
||||
displayTooltip(view, R.string.MultiselectForwardFragment__videos_will_be_trimmed) {
|
||||
SignalStore.storyValues().videoTooltipSeen = true
|
||||
}
|
||||
}
|
||||
}
|
||||
Stories.MediaTransform.SendRequirements.CAN_NOT_SEND -> {
|
||||
if (!SignalStore.storyValues().cannotSendTooltipSeen) {
|
||||
displayTooltip(view, R.string.MultiselectForwardFragment__videos_sent_to_stories_cant) {
|
||||
SignalStore.storyValues().cannotSendTooltipSeen = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resultsSet.toSet()
|
||||
}
|
||||
|
||||
private fun displayTooltip(anchor: View, @StringRes text: Int, onDismiss: () -> Unit) {
|
||||
TooltipPopup
|
||||
.forTarget(anchor)
|
||||
.setText(text)
|
||||
.setTextColor(ContextCompat.getColor(requireContext(), R.color.signal_colorOnPrimary))
|
||||
.setBackgroundTint(ContextCompat.getColor(requireContext(), R.color.signal_colorPrimary))
|
||||
.setOnDismissListener {
|
||||
onDismiss()
|
||||
}
|
||||
.show(TooltipPopup.POSITION_BELOW)
|
||||
}
|
||||
|
||||
private fun getConfiguration(contactSearchState: ContactSearchState): ContactSearchConfiguration {
|
||||
return findListener<SearchConfigurationProvider>()?.getSearchConfiguration(childFragmentManager, contactSearchState) ?: ContactSearchConfiguration.build {
|
||||
query = contactSearchState.query
|
||||
@@ -417,7 +466,7 @@ class MultiselectForwardFragment :
|
||||
}
|
||||
|
||||
private fun isSelectedMediaValidForStories(): Boolean {
|
||||
return requireListener<Callback>().canSendMediaToStories() && getMultiShareArgs().all { it.isValidForStories }
|
||||
return getMultiShareArgs().all { it.isValidForStories }
|
||||
}
|
||||
|
||||
private fun isSelectedMediaValidForNonStories(): Boolean {
|
||||
@@ -439,7 +488,7 @@ class MultiselectForwardFragment :
|
||||
fun setResult(bundle: Bundle)
|
||||
fun getContainer(): ViewGroup
|
||||
fun getDialogBackgroundColor(): Int
|
||||
fun canSendMediaToStories(): Boolean = true
|
||||
fun getStorySendRequirements(): Stories.MediaTransform.SendRequirements? = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.FullScreenDialogFragment
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||
|
||||
class MultiselectForwardFullScreenDialogFragment : FullScreenDialogFragment(), MultiselectForwardFragment.Callback {
|
||||
@@ -33,6 +34,10 @@ class MultiselectForwardFullScreenDialogFragment : FullScreenDialogFragment(), M
|
||||
return ContextCompat.getColor(requireContext(), R.color.signal_background_primary)
|
||||
}
|
||||
|
||||
override fun getStorySendRequirements(): Stories.MediaTransform.SendRequirements? {
|
||||
return findListener<Callback>()?.getStorySendRequirements()
|
||||
}
|
||||
|
||||
override fun getContainer(): ViewGroup {
|
||||
return requireView().findViewById(R.id.full_screen_dialog_content) as ViewGroup
|
||||
}
|
||||
@@ -47,12 +52,8 @@ class MultiselectForwardFullScreenDialogFragment : FullScreenDialogFragment(), M
|
||||
|
||||
override fun onSearchInputFocused() = Unit
|
||||
|
||||
override fun canSendMediaToStories(): Boolean {
|
||||
return findListener<Callback>()?.canSendMediaToStories() ?: true
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onFinishForwardAction() = Unit
|
||||
fun canSendMediaToStories(): Boolean = true
|
||||
fun getStorySendRequirements(): Stories.MediaTransform.SendRequirements? = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.conversation.mutiselect.forward
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
@@ -8,6 +9,7 @@ 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.stories.Stories
|
||||
import java.util.Optional
|
||||
|
||||
class MultiselectForwardRepository {
|
||||
@@ -18,6 +20,20 @@ class MultiselectForwardRepository {
|
||||
val onAllMessagesFailed: () -> Unit
|
||||
)
|
||||
|
||||
fun checkAllSelectedMediaCanBeSentToStories(records: List<MultiShareArgs>): Single<Stories.MediaTransform.SendRequirements> {
|
||||
if (!Stories.isFeatureEnabled() || records.isEmpty()) {
|
||||
return Single.just(Stories.MediaTransform.SendRequirements.CAN_NOT_SEND)
|
||||
}
|
||||
|
||||
return Single.fromCallable {
|
||||
if (records.any { !it.isValidForStories }) {
|
||||
Stories.MediaTransform.SendRequirements.CAN_NOT_SEND
|
||||
} else {
|
||||
Stories.MediaTransform.getSendRequirements(records.map { it.media }.flatten())
|
||||
}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
fun canSelectRecipient(recipientId: Optional<RecipientId>): Single<Boolean> {
|
||||
if (!recipientId.isPresent) {
|
||||
return Single.just(true)
|
||||
|
||||
@@ -2,10 +2,13 @@ package org.thoughtcrime.securesms.conversation.mutiselect.forward
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
|
||||
data class MultiselectForwardState(
|
||||
val stage: Stage = Stage.Selection
|
||||
val stage: Stage = Stage.Selection,
|
||||
val storySendRequirements: Stories.MediaTransform.SendRequirements = Stories.MediaTransform.SendRequirements.CAN_NOT_SEND
|
||||
) {
|
||||
|
||||
sealed class Stage {
|
||||
object Selection : Stage()
|
||||
object FirstConfirmation : Stage()
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.conversation.mutiselect.forward
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords
|
||||
@@ -18,6 +20,19 @@ class MultiselectForwardViewModel(
|
||||
private val store = Store(MultiselectForwardState())
|
||||
|
||||
val state: LiveData<MultiselectForwardState> = store.stateLiveData
|
||||
val snapshot: MultiselectForwardState get() = store.state
|
||||
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
init {
|
||||
disposables += repository.checkAllSelectedMediaCanBeSentToStories(records).subscribe { sendRequirements ->
|
||||
store.update { it.copy(storySendRequirements = sendRequirements) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
disposables.clear()
|
||||
}
|
||||
|
||||
fun send(additionalMessage: String, selectedContacts: Set<ContactSearchKey>) {
|
||||
if (SignalStore.tooltips().showMultiForwardDialog()) {
|
||||
|
||||
Reference in New Issue
Block a user