kopia lustrzana https://github.com/ryukoposting/Signal-Android
Allow media selection recipient selection fragment to display in user's chosen app theme.
rodzic
09b92a6559
commit
b20658c829
|
@ -371,6 +371,11 @@
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
||||||
|
<activity android:name=".conversation.mutiselect.forward.MultiselectForwardActivity"
|
||||||
|
android:theme="@style/Signal.DayNight.NoActionBar"
|
||||||
|
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing"
|
||||||
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
|
||||||
|
|
||||||
<activity android:name=".PassphraseChangeActivity"
|
<activity android:name=".PassphraseChangeActivity"
|
||||||
android:label="@string/AndroidManifest__change_passphrase"
|
android:label="@string/AndroidManifest__change_passphrase"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
|
@ -13,10 +13,11 @@ import org.thoughtcrime.securesms.util.DynamicTheme
|
||||||
abstract class FragmentWrapperActivity : PassphraseRequiredActivity() {
|
abstract class FragmentWrapperActivity : PassphraseRequiredActivity() {
|
||||||
|
|
||||||
protected open val dynamicTheme: DynamicTheme = DynamicNoActionBarTheme()
|
protected open val dynamicTheme: DynamicTheme = DynamicNoActionBarTheme()
|
||||||
|
protected open val contentViewId: Int = R.layout.fragment_container
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
super.onCreate(savedInstanceState, ready)
|
super.onCreate(savedInstanceState, ready)
|
||||||
setContentView(R.layout.fragment_container)
|
setContentView(contentViewId)
|
||||||
dynamicTheme.onCreate(this)
|
dynamicTheme.onCreate(this)
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.thoughtcrime.securesms.conversation.mutiselect.forward
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.FragmentWrapperActivity
|
||||||
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||||
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment.Companion.RESULT_SELECTION
|
||||||
|
|
||||||
|
class MultiselectForwardActivity : FragmentWrapperActivity(), MultiselectForwardFragment.Callback {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ARGS = "args"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val args: MultiselectForwardFragmentArgs get() = intent.getParcelableExtra(ARGS)!!
|
||||||
|
|
||||||
|
override val contentViewId: Int = R.layout.multiselect_forward_activity
|
||||||
|
|
||||||
|
override fun getFragment(): Fragment {
|
||||||
|
return MultiselectForwardFragment.create(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinishForwardAction() = Unit
|
||||||
|
|
||||||
|
override fun exitFlow() {
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSearchInputFocused() = Unit
|
||||||
|
|
||||||
|
override fun setResult(bundle: Bundle) {
|
||||||
|
setResult(RESULT_OK, Intent().putExtras(bundle))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("WrongViewCast")
|
||||||
|
override fun getContainer(): ViewGroup {
|
||||||
|
return findViewById(R.id.fragment_container_wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDialogBackgroundColor(): Int {
|
||||||
|
return ContextCompat.getColor(this, R.color.signal_colorBackground)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectionContract : ActivityResultContract<MultiselectForwardFragmentArgs, List<ContactSearchKey.RecipientSearchKey>>() {
|
||||||
|
override fun createIntent(context: Context, input: MultiselectForwardFragmentArgs): Intent {
|
||||||
|
return Intent(context, MultiselectForwardActivity::class.java).putExtra(ARGS, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?): List<ContactSearchKey.RecipientSearchKey> {
|
||||||
|
return if (resultCode != RESULT_OK) {
|
||||||
|
emptyList()
|
||||||
|
} else if (intent == null || !intent.hasExtra(RESULT_SELECTION)) {
|
||||||
|
throw IllegalStateException("Selection contract requires a selection.")
|
||||||
|
} else {
|
||||||
|
val selection: List<ContactSearchKey.ParcelableRecipientSearchKey> = intent.getParcelableArrayListExtra(RESULT_SELECTION)!!
|
||||||
|
selection.map { it.asRecipientSearchKey() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import android.widget.Toast
|
||||||
import androidx.annotation.PluralsRes
|
import androidx.annotation.PluralsRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -41,7 +42,6 @@ import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseGroupStoryBottomShe
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseStoryTypeBottomSheet
|
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseStoryTypeBottomSheet
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
|
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
|
||||||
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
|
||||||
import org.thoughtcrime.securesms.sharing.ShareSelectionAdapter
|
import org.thoughtcrime.securesms.sharing.ShareSelectionAdapter
|
||||||
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
|
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
|
@ -93,19 +93,12 @@ class MultiselectForwardFragment :
|
||||||
private var handler: Handler? = null
|
private var handler: Handler? = null
|
||||||
|
|
||||||
private fun createViewModelFactory(): MultiselectForwardViewModel.Factory {
|
private fun createViewModelFactory(): MultiselectForwardViewModel.Factory {
|
||||||
return MultiselectForwardViewModel.Factory(getMultiShareArgs(), isSelectionOnly, MultiselectForwardRepository())
|
return MultiselectForwardViewModel.Factory(args.storySendRequirements, args.multiShareArgs, args.forceSelectionOnly, MultiselectForwardRepository())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMultiShareArgs(): ArrayList<MultiShareArgs> = requireNotNull(requireArguments().getParcelableArrayList(ARG_MULTISHARE_ARGS))
|
private val args: MultiselectForwardFragmentArgs by lazy {
|
||||||
|
requireArguments().getParcelable(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)
|
|
||||||
|
|
||||||
private val sendButtonTint: Int
|
|
||||||
get() = requireArguments().getInt(ARG_SEND_BUTTON_TINT, -1)
|
|
||||||
|
|
||||||
override fun onGetLayoutInflater(savedInstanceState: Bundle?): LayoutInflater {
|
override fun onGetLayoutInflater(savedInstanceState: Bundle?): LayoutInflater {
|
||||||
return if (parentFragment != null) {
|
return if (parentFragment != null) {
|
||||||
|
@ -119,7 +112,7 @@ class MultiselectForwardFragment :
|
||||||
view.minimumHeight = resources.displayMetrics.heightPixels
|
view.minimumHeight = resources.displayMetrics.heightPixels
|
||||||
|
|
||||||
contactSearchRecycler = view.findViewById(R.id.contact_selection_list)
|
contactSearchRecycler = view.findViewById(R.id.contact_selection_list)
|
||||||
contactSearchMediator = ContactSearchMediator(this, contactSearchRecycler, FeatureFlags.shareSelectionLimit(), !isSingleRecipientSelection(), this::getConfiguration, this::filterContacts)
|
contactSearchMediator = ContactSearchMediator(this, contactSearchRecycler, FeatureFlags.shareSelectionLimit(), !args.selectSingleRecipient, this::getConfiguration, this::filterContacts)
|
||||||
|
|
||||||
callback = findListener()!!
|
callback = findListener()!!
|
||||||
disposables.bindTo(viewLifecycleOwner.lifecycle)
|
disposables.bindTo(viewLifecycleOwner.lifecycle)
|
||||||
|
@ -147,8 +140,8 @@ class MultiselectForwardFragment :
|
||||||
val sendButton: View = bottomBar.findViewById(R.id.share_confirm)
|
val sendButton: View = bottomBar.findViewById(R.id.share_confirm)
|
||||||
val backgroundHelper: View = bottomBar.findViewById(R.id.background_helper)
|
val backgroundHelper: View = bottomBar.findViewById(R.id.background_helper)
|
||||||
|
|
||||||
if (sendButtonTint != -1) {
|
if (args.sendButtonTint != -1) {
|
||||||
ViewCompat.setBackgroundTintList(sendButton, ColorStateList.valueOf(sendButtonTint))
|
ViewCompat.setBackgroundTintList(sendButton, ColorStateList.valueOf(args.sendButtonTint))
|
||||||
}
|
}
|
||||||
|
|
||||||
FullscreenHelper.configureBottomBarLayout(requireActivity(), bottomBarSpacer, bottomBar)
|
FullscreenHelper.configureBottomBarLayout(requireActivity(), bottomBarSpacer, bottomBar)
|
||||||
|
@ -156,7 +149,7 @@ class MultiselectForwardFragment :
|
||||||
backgroundHelper.setBackgroundColor(callback.getDialogBackgroundColor())
|
backgroundHelper.setBackgroundColor(callback.getDialogBackgroundColor())
|
||||||
bottomBarSpacer.setBackgroundColor(callback.getDialogBackgroundColor())
|
bottomBarSpacer.setBackgroundColor(callback.getDialogBackgroundColor())
|
||||||
|
|
||||||
title?.setText(requireArguments().getInt(ARG_TITLE))
|
title?.setText(args.title)
|
||||||
|
|
||||||
addMessage = bottomBar.findViewById(R.id.add_message)
|
addMessage = bottomBar.findViewById(R.id.add_message)
|
||||||
|
|
||||||
|
@ -174,7 +167,7 @@ class MultiselectForwardFragment :
|
||||||
onSend(it)
|
onSend(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendButton.visible = !isSingleRecipientSelection()
|
sendButton.visible = !args.selectSingleRecipient
|
||||||
|
|
||||||
shareSelectionRecycler.adapter = shareSelectionAdapter
|
shareSelectionRecycler.adapter = shareSelectionAdapter
|
||||||
|
|
||||||
|
@ -183,14 +176,14 @@ class MultiselectForwardFragment :
|
||||||
container.addView(bottomBarAndSpacer)
|
container.addView(bottomBarAndSpacer)
|
||||||
|
|
||||||
contactSearchMediator.getSelectionState().observe(viewLifecycleOwner) { contactSelection ->
|
contactSearchMediator.getSelectionState().observe(viewLifecycleOwner) { contactSelection ->
|
||||||
if (contactSelection.isNotEmpty() && isSingleRecipientSelection()) {
|
if (contactSelection.isNotEmpty() && args.selectSingleRecipient) {
|
||||||
onSend(sendButton)
|
onSend(sendButton)
|
||||||
return@observe
|
return@observe
|
||||||
}
|
}
|
||||||
|
|
||||||
shareSelectionAdapter.submitList(contactSelection.mapIndexed { index, key -> ShareSelectionMappingModel(key.requireShareContact(), index == 0) })
|
shareSelectionAdapter.submitList(contactSelection.mapIndexed { index, key -> ShareSelectionMappingModel(key.requireShareContact(), index == 0) })
|
||||||
|
|
||||||
addMessage.visible = !forceDisableAddMessage && contactSelection.any { key -> key !is ContactSearchKey.RecipientSearchKey.Story } && getMultiShareArgs().isNotEmpty()
|
addMessage.visible = !args.forceDisableAddMessage && contactSelection.any { key -> key !is ContactSearchKey.RecipientSearchKey.Story } && args.multiShareArgs.isNotEmpty()
|
||||||
|
|
||||||
if (contactSelection.isNotEmpty() && !bottomBar.isVisible) {
|
if (contactSelection.isNotEmpty() && !bottomBar.isVisible) {
|
||||||
bottomBar.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.slide_fade_from_bottom)
|
bottomBar.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.slide_fade_from_bottom)
|
||||||
|
@ -240,7 +233,7 @@ class MultiselectForwardFragment :
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val expiringMessages = getMultiShareArgs().filter { it.expiresAt > 0L }
|
val expiringMessages = args.multiShareArgs.filter { it.expiresAt > 0L }
|
||||||
val firstToExpire = expiringMessages.minByOrNull { it.expiresAt }
|
val firstToExpire = expiringMessages.minByOrNull { it.expiresAt }
|
||||||
val earliestExpiration = firstToExpire?.expiresAt ?: -1L
|
val earliestExpiration = firstToExpire?.expiresAt ?: -1L
|
||||||
|
|
||||||
|
@ -314,12 +307,12 @@ class MultiselectForwardFragment :
|
||||||
callback.exitFlow()
|
callback.exitFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMessageCount(): Int = getMultiShareArgs().size + if (addMessage.text.isNotEmpty()) 1 else 0
|
private fun getMessageCount(): Int = args.multiShareArgs.size + if (addMessage.text.isNotEmpty()) 1 else 0
|
||||||
|
|
||||||
private fun handleMessageExpired() {
|
private fun handleMessageExpired() {
|
||||||
callback.onFinishForwardAction()
|
callback.onFinishForwardAction()
|
||||||
dismissibleDialog?.dismiss()
|
dismissibleDialog?.dismiss()
|
||||||
Toast.makeText(requireContext(), resources.getQuantityString(R.plurals.MultiselectForwardFragment__couldnt_forward_messages, getMultiShareArgs().size), Toast.LENGTH_LONG).show()
|
Toast.makeText(requireContext(), resources.getQuantityString(R.plurals.MultiselectForwardFragment__couldnt_forward_messages, args.multiShareArgs.size), Toast.LENGTH_LONG).show()
|
||||||
callback.exitFlow()
|
callback.exitFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,19 +428,15 @@ class MultiselectForwardFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun includeSms(): Boolean {
|
private fun includeSms(): Boolean {
|
||||||
return Util.isDefaultSmsProvider(requireContext()) && requireArguments().getBoolean(ARG_CAN_SEND_TO_NON_PUSH)
|
return Util.isDefaultSmsProvider(requireContext()) && args.canSendToNonPush
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSingleRecipientSelection(): Boolean {
|
|
||||||
return requireArguments().getBoolean(ARG_SELECT_SINGLE_RECIPIENT, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSelectedMediaValidForStories(): Boolean {
|
private fun isSelectedMediaValidForStories(): Boolean {
|
||||||
return getMultiShareArgs().all { it.isValidForStories }
|
return args.multiShareArgs.all { it.isValidForStories }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSelectedMediaValidForNonStories(): Boolean {
|
private fun isSelectedMediaValidForNonStories(): Boolean {
|
||||||
return getMultiShareArgs().all { it.isValidForNonStories }
|
return args.multiShareArgs.all { it.isValidForNonStories }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGroupStoryClicked() {
|
override fun onGroupStoryClicked() {
|
||||||
|
@ -478,13 +467,8 @@ class MultiselectForwardFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ARG_MULTISHARE_ARGS = "multiselect.forward.fragment.arg.multishare.args"
|
const val DIALOG_TITLE = "title"
|
||||||
const val ARG_CAN_SEND_TO_NON_PUSH = "multiselect.forward.fragment.arg.can.send.to.non.push"
|
const val ARGS = "args"
|
||||||
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 ARG_SELECT_SINGLE_RECIPIENT = "multiselect.forward.framgent.select.single.recipient"
|
|
||||||
const val ARG_SEND_BUTTON_TINT = "multiselect.forward.fragment.send.button.tint"
|
|
||||||
const val RESULT_KEY = "result_key"
|
const val RESULT_KEY = "result_key"
|
||||||
const val RESULT_SELECTION = "result_selection_recipients"
|
const val RESULT_SELECTION = "result_selection_recipients"
|
||||||
const val RESULT_SENT = "result_sent"
|
const val RESULT_SENT = "result_sent"
|
||||||
|
@ -506,26 +490,14 @@ class MultiselectForwardFragment :
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun create(multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs): Fragment {
|
fun create(multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs): Fragment {
|
||||||
return MultiselectForwardFragment().apply {
|
return MultiselectForwardFragment().apply {
|
||||||
arguments = createArgumentsBundle(multiselectForwardFragmentArgs)
|
arguments = bundleOf(ARGS to multiselectForwardFragmentArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showDialogFragment(supportFragmentManager: FragmentManager, fragment: DialogFragment, multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs) {
|
private fun showDialogFragment(supportFragmentManager: FragmentManager, fragment: DialogFragment, multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs) {
|
||||||
fragment.arguments = createArgumentsBundle(multiselectForwardFragmentArgs)
|
fragment.arguments = bundleOf(ARGS to multiselectForwardFragmentArgs, DIALOG_TITLE to multiselectForwardFragmentArgs.title)
|
||||||
|
|
||||||
fragment.show(supportFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
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)
|
|
||||||
putBoolean(ARG_SELECT_SINGLE_RECIPIENT, multiselectForwardFragmentArgs.selectSingleRecipient)
|
|
||||||
putInt(ARG_SEND_BUTTON_TINT, multiselectForwardFragmentArgs.sendButtonTint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@ package org.thoughtcrime.securesms.conversation.mutiselect.forward
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Parcelable
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.signal.core.util.StreamUtil
|
import org.signal.core.util.StreamUtil
|
||||||
import org.signal.core.util.ThreadUtil
|
import org.signal.core.util.ThreadUtil
|
||||||
import org.signal.core.util.concurrent.SignalExecutors
|
import org.signal.core.util.concurrent.SignalExecutors
|
||||||
|
@ -17,6 +19,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.mediasend.Media
|
import org.thoughtcrime.securesms.mediasend.Media
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||||
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
||||||
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil
|
import org.thoughtcrime.securesms.util.MediaUtil
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
@ -31,6 +34,7 @@ import java.util.function.Consumer
|
||||||
* @param forceSelectionOnly Force the fragment to only select recipients, never actually performing the send.
|
* @param forceSelectionOnly Force the fragment to only select recipients, never actually performing the send.
|
||||||
* @param selectSingleRecipient Only allow the selection of a single recipient.
|
* @param selectSingleRecipient Only allow the selection of a single recipient.
|
||||||
*/
|
*/
|
||||||
|
@Parcelize
|
||||||
data class MultiselectForwardFragmentArgs @JvmOverloads constructor(
|
data class MultiselectForwardFragmentArgs @JvmOverloads constructor(
|
||||||
val canSendToNonPush: Boolean,
|
val canSendToNonPush: Boolean,
|
||||||
val multiShareArgs: List<MultiShareArgs> = listOf(),
|
val multiShareArgs: List<MultiShareArgs> = listOf(),
|
||||||
|
@ -38,8 +42,9 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor(
|
||||||
val forceDisableAddMessage: Boolean = false,
|
val forceDisableAddMessage: Boolean = false,
|
||||||
val forceSelectionOnly: Boolean = false,
|
val forceSelectionOnly: Boolean = false,
|
||||||
val selectSingleRecipient: Boolean = false,
|
val selectSingleRecipient: Boolean = false,
|
||||||
@ColorInt val sendButtonTint: Int = -1
|
@ColorInt val sendButtonTint: Int = -1,
|
||||||
) {
|
val storySendRequirements: Stories.MediaTransform.SendRequirements = Stories.MediaTransform.SendRequirements.CAN_NOT_SEND
|
||||||
|
) : Parcelable {
|
||||||
|
|
||||||
fun withSendButtonTint(@ColorInt sendButtonTint: Int) = copy(sendButtonTint = sendButtonTint)
|
fun withSendButtonTint(@ColorInt sendButtonTint: Int) = copy(sendButtonTint = sendButtonTint)
|
||||||
|
|
||||||
|
@ -58,7 +63,8 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor(
|
||||||
consumer.accept(
|
consumer.accept(
|
||||||
MultiselectForwardFragmentArgs(
|
MultiselectForwardFragmentArgs(
|
||||||
isMmsSupported,
|
isMmsSupported,
|
||||||
listOf(multiShareArgs)
|
listOf(multiShareArgs),
|
||||||
|
storySendRequirements = Stories.MediaTransform.SendRequirements.CAN_NOT_SEND
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +85,15 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor(
|
||||||
val canSendToNonPush: Boolean = selectedParts.all { Multiselect.canSendToNonPush(context, it) }
|
val canSendToNonPush: Boolean = selectedParts.all { Multiselect.canSendToNonPush(context, it) }
|
||||||
val multiShareArgs: List<MultiShareArgs> = conversationMessages.map { buildMultiShareArgs(context, it, selectedParts) }
|
val multiShareArgs: List<MultiShareArgs> = conversationMessages.map { buildMultiShareArgs(context, it, selectedParts) }
|
||||||
|
|
||||||
ThreadUtil.runOnMain { consumer.accept(MultiselectForwardFragmentArgs(canSendToNonPush, multiShareArgs)) }
|
ThreadUtil.runOnMain {
|
||||||
|
consumer.accept(
|
||||||
|
MultiselectForwardFragmentArgs(
|
||||||
|
canSendToNonPush,
|
||||||
|
multiShareArgs,
|
||||||
|
storySendRequirements = Stories.MediaTransform.SendRequirements.CAN_NOT_SEND
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,12 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.setFragmentResult
|
import androidx.fragment.app.setFragmentResult
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.components.FullScreenDialogFragment
|
import org.thoughtcrime.securesms.components.FullScreenDialogFragment
|
||||||
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment.Companion.DIALOG_TITLE
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
import org.thoughtcrime.securesms.util.fragments.findListener
|
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||||
|
|
||||||
class MultiselectForwardFullScreenDialogFragment : FullScreenDialogFragment(), MultiselectForwardFragment.Callback {
|
class MultiselectForwardFullScreenDialogFragment : FullScreenDialogFragment(), MultiselectForwardFragment.Callback {
|
||||||
override fun getTitle(): Int = requireArguments().getInt(MultiselectForwardFragment.ARG_TITLE)
|
override fun getTitle(): Int = requireArguments().getParcelable<MultiselectForwardFragmentArgs>(DIALOG_TITLE)!!.title
|
||||||
|
|
||||||
override fun getDialogLayoutResource(): Int = R.layout.fragment_container
|
override fun getDialogLayoutResource(): Int = R.layout.fragment_container
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
||||||
import org.thoughtcrime.securesms.sharing.MultiShareSender
|
import org.thoughtcrime.securesms.sharing.MultiShareSender
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
|
import org.whispersystems.signalservice.api.util.Preconditions
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
class MultiselectForwardRepository {
|
class MultiselectForwardRepository {
|
||||||
|
@ -21,7 +22,9 @@ class MultiselectForwardRepository {
|
||||||
)
|
)
|
||||||
|
|
||||||
fun checkAllSelectedMediaCanBeSentToStories(records: List<MultiShareArgs>): Single<Stories.MediaTransform.SendRequirements> {
|
fun checkAllSelectedMediaCanBeSentToStories(records: List<MultiShareArgs>): Single<Stories.MediaTransform.SendRequirements> {
|
||||||
if (!Stories.isFeatureEnabled() || records.isEmpty()) {
|
Preconditions.checkArgument(records.isNotEmpty())
|
||||||
|
|
||||||
|
if (!Stories.isFeatureEnabled()) {
|
||||||
return Single.just(Stories.MediaTransform.SendRequirements.CAN_NOT_SEND)
|
return Single.just(Stories.MediaTransform.SendRequirements.CAN_NOT_SEND)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,21 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords
|
import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords
|
||||||
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
import org.thoughtcrime.securesms.sharing.MultiShareArgs
|
||||||
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
import org.thoughtcrime.securesms.util.livedata.Store
|
import org.thoughtcrime.securesms.util.livedata.Store
|
||||||
|
|
||||||
class MultiselectForwardViewModel(
|
class MultiselectForwardViewModel(
|
||||||
|
private val storySendRequirements: Stories.MediaTransform.SendRequirements,
|
||||||
private val records: List<MultiShareArgs>,
|
private val records: List<MultiShareArgs>,
|
||||||
private val isSelectionOnly: Boolean,
|
private val isSelectionOnly: Boolean,
|
||||||
private val repository: MultiselectForwardRepository
|
private val repository: MultiselectForwardRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val store = Store(MultiselectForwardState())
|
private val store = Store(
|
||||||
|
MultiselectForwardState(
|
||||||
|
storySendRequirements = storySendRequirements
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
val state: LiveData<MultiselectForwardState> = store.stateLiveData
|
val state: LiveData<MultiselectForwardState> = store.stateLiveData
|
||||||
val snapshot: MultiselectForwardState get() = store.state
|
val snapshot: MultiselectForwardState get() = store.state
|
||||||
|
@ -25,8 +31,10 @@ class MultiselectForwardViewModel(
|
||||||
private val disposables = CompositeDisposable()
|
private val disposables = CompositeDisposable()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
disposables += repository.checkAllSelectedMediaCanBeSentToStories(records).subscribe { sendRequirements ->
|
if (records.isNotEmpty()) {
|
||||||
store.update { it.copy(storySendRequirements = sendRequirements) }
|
disposables += repository.checkAllSelectedMediaCanBeSentToStories(records).subscribe { sendRequirements ->
|
||||||
|
store.update { it.copy(storySendRequirements = sendRequirements) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,12 +96,13 @@ class MultiselectForwardViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(
|
class Factory(
|
||||||
|
private val storySendRequirements: Stories.MediaTransform.SendRequirements,
|
||||||
private val records: List<MultiShareArgs>,
|
private val records: List<MultiShareArgs>,
|
||||||
private val isSelectionOnly: Boolean,
|
private val isSelectionOnly: Boolean,
|
||||||
private val repository: MultiselectForwardRepository,
|
private val repository: MultiselectForwardRepository,
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return requireNotNull(modelClass.cast(MultiselectForwardViewModel(records, isSelectionOnly, repository)))
|
return requireNotNull(modelClass.cast(MultiselectForwardViewModel(storySendRequirements, records, isSelectionOnly, repository)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState
|
||||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFullScreenDialogFragment
|
|
||||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.SearchConfigurationProvider
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.SearchConfigurationProvider
|
||||||
import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
|
import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
|
||||||
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment
|
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment
|
||||||
|
@ -52,8 +51,7 @@ class MediaSelectionActivity :
|
||||||
EmojiKeyboardPageFragment.Callback,
|
EmojiKeyboardPageFragment.Callback,
|
||||||
EmojiEventListener,
|
EmojiEventListener,
|
||||||
EmojiSearchFragment.Callback,
|
EmojiSearchFragment.Callback,
|
||||||
SearchConfigurationProvider,
|
SearchConfigurationProvider {
|
||||||
MultiselectForwardFullScreenDialogFragment.Callback {
|
|
||||||
|
|
||||||
private var animateInShadowLayerValueAnimator: ValueAnimator? = null
|
private var animateInShadowLayerValueAnimator: ValueAnimator? = null
|
||||||
private var animateInTextColorValueAnimator: ValueAnimator? = null
|
private var animateInTextColorValueAnimator: ValueAnimator? = null
|
||||||
|
@ -334,10 +332,6 @@ class MediaSelectionActivity :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStorySendRequirements(): Stories.MediaTransform.SendRequirements {
|
|
||||||
return viewModel.getStorySendRequirements()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class OnBackPressed : OnBackPressedCallback(true) {
|
private inner class OnBackPressed : OnBackPressedCallback(true) {
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
val navController = Navigation.findNavController(this@MediaSelectionActivity, R.id.fragment_container)
|
val navController = Navigation.findNavController(this@MediaSelectionActivity, R.id.fragment_container)
|
||||||
|
|
|
@ -17,7 +17,6 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.setFragmentResultListener
|
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
|
@ -28,7 +27,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardActivity
|
||||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
||||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.HudCommand
|
import org.thoughtcrime.securesms.mediasend.v2.HudCommand
|
||||||
|
@ -138,16 +137,20 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
|
||||||
sharedViewModel.sendCommand(HudCommand.SaveMedia)
|
sharedViewModel.sendCommand(HudCommand.SaveMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
setFragmentResultListener(MultiselectForwardFragment.RESULT_KEY) { _, bundle ->
|
val recipientSelectionLauncher = registerForActivityResult(MultiselectForwardActivity.SelectionContract()) { keys ->
|
||||||
val parcelizedKeys: List<ContactSearchKey.ParcelableRecipientSearchKey> = bundle.getParcelableArrayList(MultiselectForwardFragment.RESULT_SELECTION)!!
|
if (keys.isNotEmpty()) {
|
||||||
val contactSearchKeys = parcelizedKeys.map { it.asRecipientSearchKey() }
|
performSend(keys)
|
||||||
performSend(contactSearchKeys)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendButton.setOnClickListener {
|
sendButton.setOnClickListener {
|
||||||
if (sharedViewModel.isContactSelectionRequired) {
|
if (sharedViewModel.isContactSelectionRequired) {
|
||||||
val args = MultiselectForwardFragmentArgs(false, title = R.string.MediaReviewFragment__send_to)
|
val args = MultiselectForwardFragmentArgs(
|
||||||
MultiselectForwardFragment.showFullScreen(parentFragmentManager, args)
|
false,
|
||||||
|
title = R.string.MediaReviewFragment__send_to,
|
||||||
|
storySendRequirements = sharedViewModel.getStorySendRequirements()
|
||||||
|
)
|
||||||
|
recipientSelectionLauncher.launch(args)
|
||||||
} else {
|
} else {
|
||||||
performSend()
|
performSend()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/fragment_container_wrapper"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</FrameLayout>
|
Ładowanie…
Reference in New Issue