Fix multiple issues in CFv2.
This commit is contained in:
parent
6be9225fbd
commit
3db83c1602
4 changed files with 105 additions and 17 deletions
|
@ -1,40 +1,101 @@
|
||||||
package org.thoughtcrime.securesms.conversation.v2
|
package org.thoughtcrime.securesms.conversation.v2
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
import android.view.Window
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.fragment.app.Fragment
|
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||||
import org.thoughtcrime.securesms.components.FragmentWrapperActivity
|
import io.reactivex.rxjava3.subjects.Subject
|
||||||
|
import org.thoughtcrime.securesms.PassphraseRequiredActivity
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentComponent
|
||||||
|
import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository
|
||||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
|
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController
|
||||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
||||||
|
import org.thoughtcrime.securesms.util.Debouncer
|
||||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper activity for ConversationFragment.
|
* Wrapper activity for ConversationFragment.
|
||||||
*/
|
*/
|
||||||
class ConversationActivity : FragmentWrapperActivity(), VoiceNoteMediaControllerOwner {
|
class ConversationActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner, DonationPaymentComponent {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val STATE_WATERMARK = "share_data_watermark"
|
||||||
|
}
|
||||||
|
|
||||||
private val theme = DynamicNoActionBarTheme()
|
private val theme = DynamicNoActionBarTheme()
|
||||||
|
private val transitionDebouncer: Debouncer = Debouncer(150, TimeUnit.MILLISECONDS)
|
||||||
|
private var shareDataTimestamp: Long = -1L
|
||||||
|
|
||||||
override val voiceNoteMediaController = VoiceNoteMediaController(this, true)
|
override val voiceNoteMediaController = VoiceNoteMediaController(this, true)
|
||||||
|
|
||||||
|
override val stripeRepository: StripeRepository by lazy { StripeRepository(this) }
|
||||||
|
override val googlePayResultPublisher: Subject<DonationPaymentComponent.GooglePayResult> = PublishSubject.create()
|
||||||
|
|
||||||
private val motionEventRelay: MotionEventRelay by viewModels()
|
private val motionEventRelay: MotionEventRelay by viewModels()
|
||||||
|
|
||||||
override fun onPreCreate() {
|
override fun onPreCreate() {
|
||||||
theme.onCreate(this)
|
theme.onCreate(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
|
supportPostponeEnterTransition()
|
||||||
|
transitionDebouncer.publish { supportStartPostponedEnterTransition() }
|
||||||
|
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
shareDataTimestamp = savedInstanceState.getLong(STATE_WATERMARK, -1L)
|
||||||
|
} else if (intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY != 0) {
|
||||||
|
shareDataTimestamp = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(R.layout.fragment_container)
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
replaceFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
theme.onResume(this)
|
theme.onResume(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFragment(): Fragment = ConversationFragment().apply {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
arguments = intent.extras
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putLong(STATE_WATERMARK, shareDataTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
transitionDebouncer.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
error("ON NEW INTENT")
|
setIntent(intent)
|
||||||
|
replaceFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
googlePayResultPublisher.onNext(DonationPaymentComponent.GooglePayResult(requestCode, resultCode, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceFragment() {
|
||||||
|
val fragment = ConversationFragment().apply {
|
||||||
|
arguments = intent.extras
|
||||||
|
}
|
||||||
|
|
||||||
|
supportFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, fragment)
|
||||||
|
.disallowAddToBackStack()
|
||||||
|
.commitNowAllowingStateLoss()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||||
|
|
|
@ -227,7 +227,6 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModelV2
|
||||||
import org.thoughtcrime.securesms.longmessage.LongMessageFragment
|
import org.thoughtcrime.securesms.longmessage.LongMessageFragment
|
||||||
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity
|
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
|
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory.create
|
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity
|
import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity
|
||||||
import org.thoughtcrime.securesms.mediasend.Media
|
import org.thoughtcrime.securesms.mediasend.Media
|
||||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
|
||||||
|
@ -436,6 +435,7 @@ class ConversationFragment :
|
||||||
private lateinit var conversationItemDecorations: ConversationItemDecorations
|
private lateinit var conversationItemDecorations: ConversationItemDecorations
|
||||||
private lateinit var optionsMenuCallback: ConversationOptionsMenuCallback
|
private lateinit var optionsMenuCallback: ConversationOptionsMenuCallback
|
||||||
private lateinit var typingIndicatorDecoration: TypingIndicatorDecoration
|
private lateinit var typingIndicatorDecoration: TypingIndicatorDecoration
|
||||||
|
private lateinit var backPressedCallback: BackPressedDelegate
|
||||||
|
|
||||||
private var animationsAllowed = false
|
private var animationsAllowed = false
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
|
@ -773,6 +773,11 @@ class ConversationFragment :
|
||||||
|
|
||||||
private fun doAfterFirstRender() {
|
private fun doAfterFirstRender() {
|
||||||
Log.d(TAG, "doAfterFirstRender")
|
Log.d(TAG, "doAfterFirstRender")
|
||||||
|
activity?.supportStartPostponedEnterTransition()
|
||||||
|
|
||||||
|
backPressedCallback = BackPressedDelegate()
|
||||||
|
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressedCallback)
|
||||||
|
|
||||||
attachmentManager = AttachmentManager(requireContext(), requireView(), AttachmentManagerListener())
|
attachmentManager = AttachmentManager(requireContext(), requireView(), AttachmentManagerListener())
|
||||||
|
|
||||||
EventBus.getDefault().registerForLifecycle(groupCallViewModel, viewLifecycleOwner)
|
EventBus.getDefault().registerForLifecycle(groupCallViewModel, viewLifecycleOwner)
|
||||||
|
@ -900,6 +905,7 @@ class ConversationFragment :
|
||||||
disposables.add(
|
disposables.add(
|
||||||
draftViewModel
|
draftViewModel
|
||||||
.state
|
.state
|
||||||
|
.distinctUntilChanged { previous, next -> previous.voiceNoteDraft == next.voiceNoteDraft }
|
||||||
.subscribe {
|
.subscribe {
|
||||||
inputPanel.voiceNoteDraft = it.voiceNoteDraft
|
inputPanel.voiceNoteDraft = it.voiceNoteDraft
|
||||||
updateToggleButtonState()
|
updateToggleButtonState()
|
||||||
|
@ -1897,6 +1903,21 @@ class ConversationFragment :
|
||||||
composeText.clearFocus()
|
composeText.clearFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class BackPressedDelegate : OnBackPressedCallback(true) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
Log.d(TAG, "onBackPressed()")
|
||||||
|
if (reactionDelegate.isShowing) {
|
||||||
|
reactionDelegate.hide()
|
||||||
|
} else if (isSearchRequested) {
|
||||||
|
searchMenuItem?.collapseActionView()
|
||||||
|
} else if (args.conversationScreenType.isInBubble) {
|
||||||
|
requireActivity().onBackPressed()
|
||||||
|
} else {
|
||||||
|
requireActivity().finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//region Message action handling
|
//region Message action handling
|
||||||
|
|
||||||
private fun handleReplyToMessage(conversationMessage: ConversationMessage) {
|
private fun handleReplyToMessage(conversationMessage: ConversationMessage) {
|
||||||
|
@ -2490,7 +2511,13 @@ class ConversationFragment :
|
||||||
|
|
||||||
override fun goToMediaPreview(parent: ConversationItem, sharedElement: View, args: MediaIntentFactory.MediaPreviewArgs) {
|
override fun goToMediaPreview(parent: ConversationItem, sharedElement: View, args: MediaIntentFactory.MediaPreviewArgs) {
|
||||||
if (this@ConversationFragment.args.conversationScreenType.isInBubble) {
|
if (this@ConversationFragment.args.conversationScreenType.isInBubble) {
|
||||||
requireActivity().startActivity(create(requireActivity(), args.skipSharedElementTransition(true)))
|
val recipient = viewModel.recipientSnapshot ?: return
|
||||||
|
val intent = ConversationIntents.createBuilderSync(requireActivity(), recipient.id, viewModel.threadId)
|
||||||
|
.withStartingPosition(binding.conversationItemRecycler.getChildAdapterPosition(parent))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
requireActivity().startActivity(intent)
|
||||||
|
requireActivity().startActivity(MediaIntentFactory.create(requireActivity(), args.skipSharedElementTransition(true)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2506,7 +2533,7 @@ class ConversationFragment :
|
||||||
sharedElement.transitionName = MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME
|
sharedElement.transitionName = MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME
|
||||||
requireActivity().setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback())
|
requireActivity().setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback())
|
||||||
val options = ActivityOptions.makeSceneTransitionAnimation(requireActivity(), sharedElement, MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME)
|
val options = ActivityOptions.makeSceneTransitionAnimation(requireActivity(), sharedElement, MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME)
|
||||||
requireActivity().startActivity(create(requireActivity(), args), options.toBundle())
|
requireActivity().startActivity(MediaIntentFactory.create(requireActivity(), args), options.toBundle())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) {
|
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) {
|
||||||
|
@ -2655,13 +2682,13 @@ class ConversationFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
clearFocusedItem()
|
|
||||||
adapter.toggleSelection(item)
|
|
||||||
binding.conversationItemRecycler.invalidateItemDecorations()
|
|
||||||
|
|
||||||
actionMode = (requireActivity() as AppCompatActivity).startSupportActionMode(actionModeCallback)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
clearFocusedItem()
|
||||||
|
adapter.toggleSelection(item)
|
||||||
|
binding.conversationItemRecycler.invalidateItemDecorations()
|
||||||
|
|
||||||
|
actionMode = (requireActivity() as AppCompatActivity).startSupportActionMode(actionModeCallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,7 +207,7 @@ class ConversationRepository(
|
||||||
identityRecordsState: IdentityRecordsState?
|
identityRecordsState: IdentityRecordsState?
|
||||||
): Completable {
|
): Completable {
|
||||||
val sendCompletable = Completable.create { emitter ->
|
val sendCompletable = Completable.create { emitter ->
|
||||||
if (body.isEmpty() && slideDeck?.containsMediaSlide() != true && preUploadResults.isEmpty()) {
|
if (body.isEmpty() && slideDeck?.containsMediaSlide() != true && preUploadResults.isEmpty() && contacts.isEmpty()) {
|
||||||
emitter.onError(InvalidMessageException("Message is empty!"))
|
emitter.onError(InvalidMessageException("Message is empty!"))
|
||||||
return@create
|
return@create
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ import kotlin.time.Duration
|
||||||
* ConversationViewModel, which operates solely off of a thread id that never changes.
|
* ConversationViewModel, which operates solely off of a thread id that never changes.
|
||||||
*/
|
*/
|
||||||
class ConversationViewModel(
|
class ConversationViewModel(
|
||||||
private val threadId: Long,
|
val threadId: Long,
|
||||||
requestedStartingPosition: Int,
|
requestedStartingPosition: Int,
|
||||||
private val repository: ConversationRepository,
|
private val repository: ConversationRepository,
|
||||||
recipientRepository: ConversationRecipientRepository,
|
recipientRepository: ConversationRecipientRepository,
|
||||||
|
|
Loading…
Add table
Reference in a new issue