mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 00:01:08 +01:00
Fix bad behavior when rotating device with message details open.
This commit is contained in:
@@ -97,7 +97,6 @@ import org.signal.mediasend.MediaSendActivityContract
|
||||
import org.thoughtcrime.securesms.backup.v2.ArchiveRestoreProgress
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.verify.VerifyBackupKeyActivity
|
||||
import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar.show
|
||||
import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsActivity
|
||||
import org.thoughtcrime.securesms.calls.log.CallLogFilter
|
||||
import org.thoughtcrime.securesms.calls.log.CallLogFragment
|
||||
import org.thoughtcrime.securesms.calls.new.NewCallActivity
|
||||
@@ -140,6 +139,7 @@ import org.thoughtcrime.securesms.main.MainContentLayoutData
|
||||
import org.thoughtcrime.securesms.main.MainMegaphoneState
|
||||
import org.thoughtcrime.securesms.main.MainNavigationBar
|
||||
import org.thoughtcrime.securesms.main.MainNavigationDetailLocation
|
||||
import org.thoughtcrime.securesms.main.MainNavigationDetailLocationEffect
|
||||
import org.thoughtcrime.securesms.main.MainNavigationListLocation
|
||||
import org.thoughtcrime.securesms.main.MainNavigationRail
|
||||
import org.thoughtcrime.securesms.main.MainNavigationViewModel
|
||||
@@ -156,7 +156,6 @@ import org.thoughtcrime.securesms.main.chatNavGraphBuilder
|
||||
import org.thoughtcrime.securesms.main.navigateToDetailLocation
|
||||
import org.thoughtcrime.securesms.main.rememberDetailNavHostController
|
||||
import org.thoughtcrime.securesms.main.rememberFocusRequester
|
||||
import org.thoughtcrime.securesms.main.rememberMainNavigationDetailLocation
|
||||
import org.thoughtcrime.securesms.main.storiesNavGraphBuilder
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
|
||||
@@ -447,7 +446,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
||||
|
||||
val chatNavGraphState = ChatNavGraphState.remember(windowSizeClass)
|
||||
val mutableInteractionSource = remember { MutableInteractionSource() }
|
||||
val mainNavigationDetailLocation by rememberMainNavigationDetailLocation(mainNavigationViewModel, chatNavGraphState::writeGraphicsLayerToBitmap)
|
||||
MainNavigationDetailLocationEffect(mainNavigationViewModel, chatNavGraphState::writeGraphicsLayerToBitmap)
|
||||
|
||||
val chatsNavHostController = rememberDetailNavHostController(
|
||||
onRequestFocus = rememberFocusRequester(
|
||||
@@ -477,24 +476,28 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
||||
storiesNavGraphBuilder()
|
||||
}
|
||||
|
||||
LaunchedEffect(mainNavigationDetailLocation) {
|
||||
LaunchedEffect(Unit) {
|
||||
mainNavigationViewModel.clearEarlyDetailLocation()
|
||||
when (mainNavigationDetailLocation) {
|
||||
is MainNavigationDetailLocation.Empty -> {
|
||||
when (mainNavigationState.currentListLocation) {
|
||||
MainNavigationListLocation.CHATS, MainNavigationListLocation.ARCHIVE -> chatsNavHostController
|
||||
MainNavigationListLocation.CALLS -> callsNavHostController
|
||||
MainNavigationListLocation.STORIES -> storiesNavHostController
|
||||
}.navigateToDetailLocation(mainNavigationDetailLocation)
|
||||
}
|
||||
mainNavigationViewModel.detailLocation.collect { location ->
|
||||
when (location) {
|
||||
is MainNavigationDetailLocation.Empty -> {
|
||||
when (mainNavigationState.currentListLocation) {
|
||||
MainNavigationListLocation.CHATS, MainNavigationListLocation.ARCHIVE -> chatsNavHostController
|
||||
MainNavigationListLocation.CALLS -> callsNavHostController
|
||||
MainNavigationListLocation.STORIES -> storiesNavHostController
|
||||
}.navigateToDetailLocation(location)
|
||||
}
|
||||
|
||||
is MainNavigationDetailLocation.Chats -> {
|
||||
chatNavGraphState.writeGraphicsLayerToBitmap()
|
||||
chatsNavHostController.navigateToDetailLocation(mainNavigationDetailLocation)
|
||||
}
|
||||
is MainNavigationDetailLocation.Chats -> {
|
||||
if (location is MainNavigationDetailLocation.Chats.Conversation) {
|
||||
chatNavGraphState.writeGraphicsLayerToBitmap()
|
||||
}
|
||||
chatsNavHostController.navigateToDetailLocation(location)
|
||||
}
|
||||
|
||||
is MainNavigationDetailLocation.Calls -> callsNavHostController.navigateToDetailLocation(mainNavigationDetailLocation)
|
||||
is MainNavigationDetailLocation.Stories -> storiesNavHostController.navigateToDetailLocation(mainNavigationDetailLocation)
|
||||
is MainNavigationDetailLocation.Calls -> callsNavHostController.navigateToDetailLocation(location)
|
||||
is MainNavigationDetailLocation.Stories -> storiesNavHostController.navigateToDetailLocation(location)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,27 +755,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
||||
val coroutine = rememberCoroutineScope()
|
||||
|
||||
return remember(scaffoldNavigator, coroutine) {
|
||||
mainNavigationViewModel.wrapNavigator(coroutine, scaffoldNavigator) { detailLocation ->
|
||||
when (detailLocation) {
|
||||
is MainNavigationDetailLocation.Chats.Conversation -> {
|
||||
startActivity(
|
||||
ConversationIntents.createBuilderSync(this, detailLocation.conversationArgs.recipientId, detailLocation.conversationArgs.threadId)
|
||||
.withArgs(detailLocation.conversationArgs)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
is MainNavigationDetailLocation.Calls.CallLinks.CallLinkDetails -> {
|
||||
startActivity(CallLinkDetailsActivity.createIntent(this, detailLocation.callLinkRoomId))
|
||||
}
|
||||
|
||||
is MainNavigationDetailLocation.Calls.CallLinks.EditCallLinkName -> {
|
||||
error("Unexpected subroute EditCallLinkName.")
|
||||
}
|
||||
|
||||
MainNavigationDetailLocation.Empty -> Unit
|
||||
}
|
||||
}
|
||||
mainNavigationViewModel.wrapNavigator(coroutine, scaffoldNavigator)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,10 @@ public abstract class FullScreenDialogFragment extends DialogFragment {
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
WindowUtil.initializeScreenshotSecurity(requireContext(), requireDialog().getWindow());
|
||||
|
||||
if (getShowsDialog()) {
|
||||
WindowUtil.initializeScreenshotSecurity(requireContext(), requireDialog().getWindow());
|
||||
}
|
||||
}
|
||||
|
||||
protected void onNavigateUp() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.signal.core.ui.initializeScreenshotSecurity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||
|
||||
@@ -57,6 +58,11 @@ abstract class WrapperDialogFragment : DialogFragment(R.layout.fragment_containe
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
dialog?.window?.initializeScreenshotSecurity()
|
||||
}
|
||||
|
||||
open fun onHandleBackPressed() {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
@@ -30,26 +30,28 @@ class CheckoutNavHostFragment : NavHostFragment() {
|
||||
get() = requireArguments().getSerializableCompat(ARG_TYPE, InAppPaymentType::class.java)!!
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (savedInstanceState == null) {
|
||||
val navGraph = navController.navInflater.inflate(R.navigation.checkout)
|
||||
navGraph.setStartDestination(
|
||||
when (inAppPaymentType) {
|
||||
InAppPaymentType.UNKNOWN -> error("Unsupported start destination")
|
||||
InAppPaymentType.ONE_TIME_GIFT -> R.id.giftFlowStartFragment
|
||||
InAppPaymentType.ONE_TIME_DONATION, InAppPaymentType.RECURRING_DONATION -> R.id.donateToSignalFragment
|
||||
InAppPaymentType.RECURRING_BACKUP -> error("Unsupported start destination")
|
||||
}
|
||||
)
|
||||
val navGraph = navController.navInflater.inflate(R.navigation.checkout)
|
||||
navGraph.setStartDestination(
|
||||
when (inAppPaymentType) {
|
||||
InAppPaymentType.UNKNOWN -> error("Unsupported start destination")
|
||||
InAppPaymentType.ONE_TIME_GIFT -> R.id.giftFlowStartFragment
|
||||
InAppPaymentType.ONE_TIME_DONATION, InAppPaymentType.RECURRING_DONATION -> R.id.donateToSignalFragment
|
||||
InAppPaymentType.RECURRING_BACKUP -> error("Unsupported start destination")
|
||||
}
|
||||
)
|
||||
|
||||
val startBundle = when (inAppPaymentType) {
|
||||
val startBundle = if (savedInstanceState == null) {
|
||||
when (inAppPaymentType) {
|
||||
InAppPaymentType.UNKNOWN -> error("Unknown payment type")
|
||||
InAppPaymentType.ONE_TIME_GIFT, InAppPaymentType.RECURRING_BACKUP -> null
|
||||
InAppPaymentType.ONE_TIME_DONATION, InAppPaymentType.RECURRING_DONATION -> DonateToSignalFragmentArgs.Builder(inAppPaymentType).build().toBundle()
|
||||
}
|
||||
|
||||
navController.setGraph(navGraph, startBundle)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
navController.setGraph(navGraph, startBundle)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentResultListener
|
||||
import androidx.fragment.app.activityViewModels
|
||||
@@ -279,6 +280,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModelV2
|
||||
import org.thoughtcrime.securesms.longmessage.LongMessageFragment
|
||||
import org.thoughtcrime.securesms.main.MainNavigationDetailLocation
|
||||
import org.thoughtcrime.securesms.main.MainNavigationListLocation
|
||||
import org.thoughtcrime.securesms.main.MainNavigationViewModel
|
||||
import org.thoughtcrime.securesms.main.MainSnackbarHostKey
|
||||
@@ -412,6 +414,7 @@ class ConversationFragment :
|
||||
private const val ACTION_PINNED_SHORTCUT = "action_pinned_shortcut"
|
||||
private const val SAVED_STATE_IS_SEARCH_REQUESTED = "is_search_requested"
|
||||
private const val EMOJI_SEARCH_FRAGMENT_TAG = "EmojiSearchFragment"
|
||||
private const val MESSAGE_DETAILS_TAG = "MessageDetailsFragment"
|
||||
|
||||
private const val SCROLL_HEADER_ANIMATION_DURATION: Long = 100L
|
||||
private const val SCROLL_HEADER_CLOSE_DELAY: Long = SCROLL_HEADER_ANIMATION_DURATION * 4
|
||||
@@ -780,6 +783,10 @@ class ConversationFragment :
|
||||
}
|
||||
keyboardEvents = null
|
||||
|
||||
if (!requireActivity().isChangingConfigurations) {
|
||||
(requireActivity().supportFragmentManager.findFragmentByTag(MESSAGE_DETAILS_TAG) as? DialogFragment)?.dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
super.onDestroyView()
|
||||
if (pinnedShortcutReceiver != null) {
|
||||
requireActivity().unregisterReceiver(pinnedShortcutReceiver)
|
||||
@@ -2793,7 +2800,11 @@ class ConversationFragment :
|
||||
|
||||
private fun handleDisplayDetails(conversationMessage: ConversationMessage) {
|
||||
val recipientSnapshot = viewModel.recipientSnapshot ?: return
|
||||
MessageDetailsFragment.create(conversationMessage.messageRecord, recipientSnapshot.id).show(childFragmentManager, null)
|
||||
if (requireActivity() is MainActivity) {
|
||||
mainNavigationViewModel.goTo(MainNavigationDetailLocation.Chats.MessageDetails(recipientSnapshot.id, conversationMessage.messageRecord.id))
|
||||
} else {
|
||||
MessageDetailsFragment.create(conversationMessage.messageRecord, recipientSnapshot.id).show(requireActivity().supportFragmentManager, MESSAGE_DETAILS_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDeleteMessages(messageParts: Set<MultiselectPart>) {
|
||||
@@ -3309,8 +3320,10 @@ class ConversationFragment :
|
||||
.show(childFragmentManager)
|
||||
} else if (messageRecord.hasFailedWithNetworkFailures()) {
|
||||
ConversationDialogs.displayMessageCouldNotBeSentDialog(requireContext(), messageRecord)
|
||||
} else if (requireActivity() is MainActivity) {
|
||||
mainNavigationViewModel.goTo(MainNavigationDetailLocation.Chats.MessageDetails(recipientId, messageRecord.id))
|
||||
} else {
|
||||
MessageDetailsFragment.create(messageRecord, recipientId).show(childFragmentManager, null)
|
||||
MessageDetailsFragment.create(messageRecord, recipientId).show(requireActivity().supportFragmentManager, MESSAGE_DETAILS_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -50,6 +52,8 @@ import org.thoughtcrime.securesms.compose.FragmentBackPressedState
|
||||
import org.thoughtcrime.securesms.conversation.ConversationArgs
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationFragment
|
||||
import org.thoughtcrime.securesms.messagedetails.MessageDetailsFragment
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.serialization.JsonSerializableNavType
|
||||
import org.thoughtcrime.securesms.window.AppScaffoldAnimationDefaults
|
||||
import org.thoughtcrime.securesms.window.AppScaffoldAnimationState
|
||||
@@ -73,8 +77,8 @@ fun NavGraphBuilder.chatNavGraphBuilder(
|
||||
val context = LocalContext.current
|
||||
|
||||
// Because it can take a long time to load content, we use a "fake" chat list image to delay displaying
|
||||
// the fragment and prevent pop-in
|
||||
var shouldDisplayFragment by remember { mutableStateOf(false) }
|
||||
// the fragment and prevent pop-in. When there's no bitmap (e.g. returning from a sub-route), skip the animation.
|
||||
var shouldDisplayFragment by remember { mutableStateOf(chatNavGraphState.chatBitmap == null) }
|
||||
val transition: Transition<Boolean> = updateTransition(shouldDisplayFragment)
|
||||
val bitmap = chatNavGraphState.chatBitmap
|
||||
|
||||
@@ -126,16 +130,42 @@ fun NavGraphBuilder.chatNavGraphBuilder(
|
||||
fragment.viewLifecycleOwner.lifecycleScope.launch {
|
||||
fragment.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
fragment.didFirstFrameRender.collectLatest {
|
||||
shouldDisplayFragment = it
|
||||
if (!it) {
|
||||
delay(150.milliseconds)
|
||||
shouldDisplayFragment = true
|
||||
if (!shouldDisplayFragment) {
|
||||
shouldDisplayFragment = it
|
||||
if (!it) {
|
||||
delay(150.milliseconds)
|
||||
shouldDisplayFragment = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composable<MainNavigationDetailLocation.Chats.MessageDetails>(
|
||||
typeMap = mapOf(
|
||||
typeOf<RecipientId>() to JsonSerializableNavType(RecipientId.serializer())
|
||||
)
|
||||
) { navBackStackEntry ->
|
||||
val context = LocalContext.current
|
||||
val route = navBackStackEntry.toRoute<MainNavigationDetailLocation.Chats.MessageDetails>()
|
||||
val fragmentState = key(route) { rememberFragmentState() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
(context as? MainNavigator.NavigatorProvider)?.onFirstRender()
|
||||
}
|
||||
|
||||
AndroidFragment(
|
||||
clazz = MessageDetailsFragment::class.java,
|
||||
fragmentState = fragmentState,
|
||||
arguments = MessageDetailsFragment.args(route.recipientId, route.messageId),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.statusBarsPadding()
|
||||
.navigationBarsPadding()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -17,7 +17,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -65,10 +64,10 @@ fun EmptyDetailScreen() {
|
||||
* utilizing collectAsStateWithLifecycle. Then the latest value is remembered as a saveable using the default [MainNavigationDetailLocation.Saver]
|
||||
*/
|
||||
@Composable
|
||||
fun rememberMainNavigationDetailLocation(
|
||||
fun MainNavigationDetailLocationEffect(
|
||||
mainNavigationViewModel: MainNavigationViewModel,
|
||||
onWillFocusPrimary: suspend () -> Unit = {}
|
||||
): State<MainNavigationDetailLocation> {
|
||||
) {
|
||||
val state = rememberSaveable(
|
||||
stateSaver = MainNavigationDetailLocation.Saver(
|
||||
mainNavigationViewModel.earlyNavigationDetailLocationRequested
|
||||
@@ -84,7 +83,9 @@ fun rememberMainNavigationDetailLocation(
|
||||
if (it == MainNavigationDetailLocation.Empty) {
|
||||
ThreePaneScaffoldRole.Secondary
|
||||
} else {
|
||||
onWillFocusPrimary()
|
||||
if (it.isContentRoot) {
|
||||
onWillFocusPrimary()
|
||||
}
|
||||
ThreePaneScaffoldRole.Primary
|
||||
}
|
||||
)
|
||||
@@ -93,8 +94,6 @@ fun rememberMainNavigationDetailLocation(
|
||||
state.value = it
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -147,6 +146,7 @@ fun rememberDetailNavHostController(
|
||||
|
||||
fun NavHostController.navigateToDetailLocation(location: MainNavigationDetailLocation) {
|
||||
navigate(location) {
|
||||
launchSingleTop = true
|
||||
if (location.isContentRoot) {
|
||||
popUpTo(graph.id) { inclusive = true }
|
||||
}
|
||||
|
||||
@@ -65,6 +65,13 @@ sealed class MainNavigationDetailLocation : Parcelable {
|
||||
@IgnoredOnParcel
|
||||
override val controllerKey: RecipientId = conversationArgs.recipientId
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class MessageDetails(val recipientId: RecipientId, val messageId: Long) : Chats() {
|
||||
@Transient
|
||||
@IgnoredOnParcel
|
||||
override val controllerKey: RecipientId = recipientId
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,7 +66,6 @@ class MainNavigationViewModel(
|
||||
|
||||
private var navigator: AppScaffoldNavigator<Any>? = null
|
||||
private var navigatorScope: CoroutineScope? = null
|
||||
private var goToLegacyDetailLocation: ((MainNavigationDetailLocation) -> Unit)? = null
|
||||
|
||||
private val internalDetailLocation = MutableSharedFlow<MainNavigationDetailLocation>()
|
||||
val detailLocation: SharedFlow<MainNavigationDetailLocation> = internalDetailLocation
|
||||
@@ -159,8 +158,7 @@ class MainNavigationViewModel(
|
||||
* Sets the navigator on the view-model. This wraps the given navigator in our own delegating implementation
|
||||
* such that we can react to navigateTo/Back signals and maintain proper state for internalDetailLocation.
|
||||
*/
|
||||
fun wrapNavigator(composeScope: CoroutineScope, threePaneScaffoldNavigator: ThreePaneScaffoldNavigator<Any>, goToLegacyDetailLocation: (MainNavigationDetailLocation) -> Unit): AppScaffoldNavigator<Any> {
|
||||
this.goToLegacyDetailLocation = goToLegacyDetailLocation
|
||||
fun wrapNavigator(composeScope: CoroutineScope, threePaneScaffoldNavigator: ThreePaneScaffoldNavigator<Any>): AppScaffoldNavigator<Any> {
|
||||
this.navigatorScope = composeScope
|
||||
this.navigator = Nav(threePaneScaffoldNavigator)
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package org.thoughtcrime.securesms.messagedetails
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@@ -15,7 +18,7 @@ import com.bumptech.glide.RequestManager
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.ringrtc.CallLinkRootKey
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.FullScreenDialogFragment
|
||||
import org.thoughtcrime.securesms.components.WrapperDialogFragment
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState
|
||||
import org.thoughtcrime.securesms.contactshare.Contact
|
||||
@@ -46,7 +49,7 @@ import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||
import org.thoughtcrime.securesms.util.Material3OnScrollHelper
|
||||
import org.thoughtcrime.securesms.util.fragments.requireListener
|
||||
|
||||
class MessageDetailsFragment : FullScreenDialogFragment(), MessageDetailsAdapter.Callbacks {
|
||||
class MessageDetailsFragment : Fragment(), MessageDetailsAdapter.Callbacks {
|
||||
private lateinit var requestManager: RequestManager
|
||||
private lateinit var viewModel: MessageDetailsViewModel
|
||||
private lateinit var adapter: MessageDetailsAdapter
|
||||
@@ -55,9 +58,16 @@ class MessageDetailsFragment : FullScreenDialogFragment(), MessageDetailsAdapter
|
||||
|
||||
private fun getVoiceNoteMediaController() = requireListener<VoiceNoteMediaControllerOwner>().voiceNoteMediaController
|
||||
|
||||
override fun getTitle() = R.string.AndroidManifest__message_details
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val view = inflater.inflate(R.layout.full_screen_dialog_fragment, container, false)
|
||||
inflater.inflate(R.layout.message_details_fragment, view.findViewById(R.id.full_screen_dialog_content), true)
|
||||
|
||||
override fun getDialogLayoutResource() = R.layout.message_details_fragment
|
||||
val toolbar: Toolbar = view.findViewById(R.id.full_screen_dialog_toolbar)
|
||||
toolbar.setTitle(R.string.AndroidManifest__message_details)
|
||||
toolbar.setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() }
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requestManager = Glide.with(this)
|
||||
@@ -67,16 +77,6 @@ class MessageDetailsFragment : FullScreenDialogFragment(), MessageDetailsAdapter
|
||||
initializeVideoPlayer(view)
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
|
||||
if (activity is Callback) {
|
||||
(activity as Callback?)!!.onMessageDetailsFragmentDismissed()
|
||||
} else if (parentFragment is Callback) {
|
||||
(parentFragment as Callback?)!!.onMessageDetailsFragmentDismissed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeList(view: View) {
|
||||
val list = view.findViewById<RecyclerView>(R.id.message_details_list)
|
||||
val toolbarShadow = view.findViewById<View>(R.id.toolbar_shadow)
|
||||
@@ -96,14 +96,14 @@ class MessageDetailsFragment : FullScreenDialogFragment(), MessageDetailsAdapter
|
||||
val factory = MessageDetailsViewModel.Factory(recipientId, messageId)
|
||||
|
||||
viewModel = ViewModelProvider(this, factory)[MessageDetailsViewModel::class.java]
|
||||
viewModel.messageDetails.observe(this) { details: MessageDetails? ->
|
||||
viewModel.messageDetails.observe(viewLifecycleOwner) { details: MessageDetails? ->
|
||||
if (details == null) {
|
||||
dismissAllowingStateLoss()
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
} else {
|
||||
adapter.submitList(convertToRows(details))
|
||||
}
|
||||
}
|
||||
viewModel.recipient.observe(this) { recipient: Recipient -> recyclerViewColorizer.setChatColors(recipient.chatColors) }
|
||||
viewModel.recipient.observe(viewLifecycleOwner) { recipient: Recipient -> recyclerViewColorizer.setChatColors(recipient.chatColors) }
|
||||
}
|
||||
|
||||
private fun initializeVideoPlayer(view: View) {
|
||||
@@ -413,8 +413,12 @@ class MessageDetailsFragment : FullScreenDialogFragment(), MessageDetailsAdapter
|
||||
Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onMessageDetailsFragmentDismissed()
|
||||
class Dialog : WrapperDialogFragment() {
|
||||
override fun getWrappedFragment(): Fragment {
|
||||
return MessageDetailsFragment().apply {
|
||||
arguments = this@Dialog.requireArguments()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -422,16 +426,17 @@ class MessageDetailsFragment : FullScreenDialogFragment(), MessageDetailsAdapter
|
||||
private const val MESSAGE_ID_EXTRA = "message_id"
|
||||
private const val RECIPIENT_EXTRA = "recipient_id"
|
||||
|
||||
fun create(message: MessageRecord, recipientId: RecipientId): DialogFragment {
|
||||
val dialogFragment: DialogFragment = MessageDetailsFragment()
|
||||
val args = Bundle()
|
||||
fun args(recipientId: RecipientId, messageId: Long): Bundle {
|
||||
return bundleOf(
|
||||
MESSAGE_ID_EXTRA to messageId,
|
||||
RECIPIENT_EXTRA to recipientId
|
||||
)
|
||||
}
|
||||
|
||||
args.putLong(MESSAGE_ID_EXTRA, message.id)
|
||||
args.putParcelable(RECIPIENT_EXTRA, recipientId)
|
||||
|
||||
dialogFragment.arguments = args
|
||||
|
||||
return dialogFragment
|
||||
fun create(message: MessageRecord, recipientId: RecipientId): Dialog {
|
||||
return Dialog().apply {
|
||||
arguments = args(recipientId, message.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user