@@ -45,11 +45,13 @@ import androidx.core.view.isVisible
4545import androidx.core.view.updateLayoutParams
4646import androidx.fragment.app.DialogFragment
4747import androidx.lifecycle.Lifecycle
48+ import androidx.lifecycle.LiveData
4849import androidx.lifecycle.Observer
4950import androidx.lifecycle.ViewModelProvider
5051import androidx.lifecycle.flowWithLifecycle
5152import androidx.lifecycle.lifecycleScope
5253import androidx.lifecycle.repeatOnLifecycle
54+ import androidx.lifecycle.viewModelScope
5355import androidx.loader.app.LoaderManager
5456import androidx.loader.content.Loader
5557import androidx.recyclerview.widget.LinearLayoutManager
@@ -76,6 +78,7 @@ import kotlinx.coroutines.flow.filterNotNull
7678import kotlinx.coroutines.flow.first
7779import kotlinx.coroutines.flow.map
7880import kotlinx.coroutines.flow.mapNotNull
81+ import kotlinx.coroutines.flow.onStart
7982import kotlinx.coroutines.flow.receiveAsFlow
8083import kotlinx.coroutines.launch
8184import kotlinx.coroutines.withContext
@@ -378,7 +381,8 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
378381 private val adapter by lazy {
379382 val adapter = ConversationAdapter (
380383 this ,
381- storage.getLastSeen(viewModel.threadId),
384+ originalLastSeen = viewModel.threadId
385+ ?.let { storage.getLastSeen(it) },
382386 false ,
383387 onItemPress = { message, position, view, event ->
384388 handlePress(message, position, view, event)
@@ -638,8 +642,10 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
638642 try {
639643 when (it) {
640644 is Long -> {
641- if (storage.getLastSeen(viewModel.threadId) < it) {
642- storage.markConversationAsRead(viewModel.threadId, it)
645+ viewModel.threadId?.let { threadId ->
646+ if (storage.getLastSeen(threadId) < it) {
647+ storage.markConversationAsRead(threadId, it)
648+ }
643649 }
644650 }
645651
@@ -661,8 +667,7 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
661667 viewModel.conversationReloadNotification
662668 .collect {
663669 if (! firstLoad.get()) {
664- LoaderManager .getInstance(this @ConversationActivityV2)
665- .restartLoader(0 , null , this @ConversationActivityV2)
670+ restartConversationLoader()
666671 }
667672 }
668673 }
@@ -672,6 +677,10 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
672677 setupUiEventsObserver()
673678 }
674679
680+ private fun restartConversationLoader () {
681+ LoaderManager .getInstance(this ).restartLoader(0 , null , this )
682+ }
683+
675684 private fun startConversationLoaderWithDelay () {
676685 conversationLoadAnimationJob = lifecycleScope.launch {
677686 delay(700 )
@@ -782,7 +791,9 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
782791
783792 override fun onResume () {
784793 super .onResume()
785- messageNotifier.setVisibleThread(viewModel.threadId)
794+ viewModel.threadId?.let { threadId ->
795+ messageNotifier.setVisibleThread(threadId)
796+ }
786797 }
787798
788799 override fun onPause () {
@@ -844,11 +855,13 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
844855 )
845856 }
846857
847- if (isUnread) {
848- storage.markConversationAsRead(
849- viewModel.threadId,
850- clock.currentTimeMillis()
851- )
858+ viewModel.threadId?.let { threadId ->
859+ if (isUnread) {
860+ storage.markConversationAsRead(
861+ threadId,
862+ clock.currentTimeMillis()
863+ )
864+ }
852865 }
853866 }
854867 }
@@ -882,7 +895,7 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
882895 val layoutManager = LinearLayoutManager (this , LinearLayoutManager .VERTICAL , false )
883896 binding.conversationRecyclerView.layoutManager = layoutManager
884897 // Workaround for the fact that CursorRecyclerViewAdapter doesn't auto-update automatically (even though it says it will)
885- LoaderManager .getInstance( this ).restartLoader( 0 , null , this )
898+ restartConversationLoader( )
886899 binding.conversationRecyclerView.addOnScrollListener(object : RecyclerView .OnScrollListener () {
887900
888901 override fun onScrolled (recyclerView : RecyclerView , dx : Int , dy : Int ) {
@@ -1026,19 +1039,45 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
10261039 }
10271040
10281041 // called from onCreate
1042+ private var typistsLiveData: LiveData <TypingStatusRepository .TypingState >? = null
10291043 private fun setUpTypingObserver () {
1030- typingStatusRepository.getTypists(viewModel.threadId).observe(this ) { state ->
1031- val recipients = if (state != null ) state.typists else listOf ()
1032- // FIXME: Also checking isScrolledToBottom is a quick fix for an issue where the
1033- // typing indicator overlays the recycler view when scrolled up
1034- val viewContainer = binding.typingIndicatorViewContainer
1035- viewContainer.isVisible = recipients.isNotEmpty() && isScrolledToBottom
1036- viewContainer.setTypists(recipients)
1044+ // Observe typists only when we have a real threadId,
1045+ // and swap if it changes, example: message request was created then accepted
1046+ lifecycleScope.launch {
1047+ repeatOnLifecycle(Lifecycle .State .STARTED ) {
1048+ viewModel.threadIdFlow
1049+ .collect { threadId ->
1050+ // Detach any previous observer
1051+ typistsLiveData?.removeObservers(this @ConversationActivityV2)
1052+ typistsLiveData = null
1053+
1054+ if (threadId == null ) {
1055+ binding.typingIndicatorViewContainer.isVisible = false
1056+ return @collect
1057+ }
1058+
1059+ // Attach observer for the real threadId
1060+ typistsLiveData =
1061+ typingStatusRepository.getTypists(threadId).also { liveData ->
1062+ liveData.observe(this @ConversationActivityV2) { state ->
1063+ val recipients = state?.typists ? : emptyList()
1064+
1065+ // Quick-fix behavior kept as-is
1066+ val viewContainer = binding.typingIndicatorViewContainer
1067+ viewContainer.isVisible =
1068+ recipients.isNotEmpty() && isScrolledToBottom
1069+ viewContainer.setTypists(recipients)
1070+ }
1071+ }
1072+ }
1073+ }
10371074 }
10381075 if (textSecurePreferences.isTypingIndicatorsEnabled()) {
10391076 binding.inputBar.addTextChangedListener {
1040- if (it.isNotEmpty()) {
1041- typingStatusSender.onTypingStarted(viewModel.threadId)
1077+ if (it.isNotEmpty()) {
1078+ viewModel.threadId?.let { threadId ->
1079+ typingStatusSender.onTypingStarted(threadId)
1080+ }
10421081 }
10431082 }
10441083 }
@@ -1241,7 +1280,10 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
12411280 return
12421281 }
12431282
1244- val lastSeenTimestamp = threadDb.getLastSeenAndHasSent(viewModel.threadId).first()
1283+ val threadId = viewModel.threadId
1284+ if (threadId == null ) return // Maybe don't scroll
1285+
1286+ val lastSeenTimestamp = threadDb.getLastSeenAndHasSent(threadId).first()
12451287 val lastSeenItemPosition = adapter.findLastSeenItemPosition(lastSeenTimestamp) ? : return
12461288
12471289 binding.conversationRecyclerView.runWhenLaidOut {
@@ -1250,7 +1292,6 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
12501292 ((layoutManager?.height ? : 0 ) / 2 )
12511293 )
12521294 }
1253-
12541295 }
12551296
12561297 private fun highlightViewAtPosition (position : Int ) {
@@ -1847,7 +1888,7 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
18471888 messageSender.send(reactionMessage, recipient.address)
18481889 }
18491890
1850- LoaderManager .getInstance( this ).restartLoader( 0 , null , this )
1891+ restartConversationLoader( )
18511892 }
18521893 }
18531894
@@ -1903,7 +1944,8 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
19031944 } else {
19041945 messageSender.send(message, recipient.address)
19051946 }
1906- LoaderManager .getInstance(this ).restartLoader(0 , null , this )
1947+
1948+ restartConversationLoader()
19071949 }
19081950 }
19091951
@@ -2197,8 +2239,8 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
21972239 waitForApprovalJobToBeSubmitted()
21982240 messageSender.send(message, recipient.address)
21992241 }
2200- // Send a typing stopped message
2201- typingStatusSender.onTypingStopped(viewModel.threadId )
2242+
2243+ stopTyping( )
22022244 return Pair (recipient.address, sentTimestamp)
22032245 }
22042246
@@ -2294,11 +2336,17 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
22942336 }
22952337 }
22962338
2297- // Send a typing stopped message
2298- typingStatusSender.onTypingStopped(viewModel.threadId)
2339+ stopTyping()
22992340 return Pair (recipient.address, sentTimestamp)
23002341 }
23012342
2343+ private fun stopTyping (){
2344+ // Send a typing stopped message
2345+ viewModel.threadId?.let { threadId ->
2346+ typingStatusSender.onTypingStopped(threadId)
2347+ }
2348+ }
2349+
23022350 private fun showGIFPicker () {
23032351 val hasSeenGIFMetaDataWarning: Boolean = textSecurePreferences.hasSeenGIFMetaDataWarning()
23042352 if (! hasSeenGIFMetaDataWarning) {
@@ -2849,9 +2897,11 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
28492897 }
28502898
28512899 fun onSearchQueryUpdated (query : String ) {
2852- binding.searchBottomBar.showLoading()
2853- searchViewModel.onQueryUpdated(query, viewModel.threadId)
2854- adapter.onSearchQueryUpdated(query.takeUnless { it.length < 2 })
2900+ viewModel.threadId?.let { threadId ->
2901+ binding.searchBottomBar.showLoading()
2902+ searchViewModel.onQueryUpdated(query, threadId)
2903+ adapter.onSearchQueryUpdated(query.takeUnless { it.length < 2 })
2904+ }
28552905 }
28562906
28572907 override fun onSearchMoveUpPressed () {
@@ -2863,9 +2913,11 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
28632913 }
28642914
28652915 private fun jumpToMessage (author : Address , timestamp : Long , highlight : Boolean , onMessageNotFound : Runnable ? ) {
2866- SimpleTask .run (lifecycle, {
2867- mmsSmsDb.getMessagePositionInConversation(viewModel.threadId, timestamp, author, false )
2868- }) { p: Int -> moveToMessagePosition(p, highlight, onMessageNotFound) }
2916+ viewModel.threadId?.let { threadId ->
2917+ SimpleTask .run (lifecycle, {
2918+ mmsSmsDb.getMessagePositionInConversation(threadId, timestamp, author, false )
2919+ }) { p: Int -> moveToMessagePosition(p, highlight, onMessageNotFound) }
2920+ }
28692921 }
28702922
28712923 private fun moveToMessagePosition (position : Int , highlight : Boolean , onMessageNotFound : Runnable ? ) {
0 commit comments