mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 12:38:33 +00:00
Remove MainListHostFragment and rescope list vms to the activity.
This commit is contained in:
@@ -13,6 +13,7 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.SystemBarStyle
|
import androidx.activity.SystemBarStyle
|
||||||
@@ -37,11 +38,11 @@ import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
|
|||||||
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
|
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
|
||||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -50,18 +51,24 @@ import androidx.fragment.app.DialogFragment
|
|||||||
import androidx.fragment.compose.AndroidFragment
|
import androidx.fragment.compose.AndroidFragment
|
||||||
import androidx.fragment.compose.rememberFragmentState
|
import androidx.fragment.compose.rememberFragmentState
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.signal.core.ui.compose.theme.SignalTheme
|
import org.signal.core.ui.compose.theme.SignalTheme
|
||||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||||
import org.signal.core.util.getSerializableCompat
|
import org.signal.core.util.getSerializableCompat
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
import org.signal.donations.StripeApi
|
import org.signal.donations.StripeApi
|
||||||
import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar.show
|
import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar.show
|
||||||
import org.thoughtcrime.securesms.calls.log.CallLogFilter
|
import org.thoughtcrime.securesms.calls.log.CallLogFilter
|
||||||
|
import org.thoughtcrime.securesms.calls.log.CallLogFragment
|
||||||
import org.thoughtcrime.securesms.calls.new.NewCallActivity
|
import org.thoughtcrime.securesms.calls.new.NewCallActivity
|
||||||
import org.thoughtcrime.securesms.components.ConnectivityWarningBottomSheet
|
import org.thoughtcrime.securesms.components.ConnectivityWarningBottomSheet
|
||||||
import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment
|
import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment
|
||||||
@@ -76,14 +83,16 @@ import org.thoughtcrime.securesms.conversation.ConversationIntents
|
|||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationFragment
|
import org.thoughtcrime.securesms.conversation.v2.ConversationFragment
|
||||||
import org.thoughtcrime.securesms.conversation.v2.MotionEventRelay
|
import org.thoughtcrime.securesms.conversation.v2.MotionEventRelay
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ShareDataTimestampViewModel
|
import org.thoughtcrime.securesms.conversation.v2.ShareDataTimestampViewModel
|
||||||
|
import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment
|
||||||
|
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment
|
||||||
import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment
|
import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment
|
||||||
import org.thoughtcrime.securesms.conversationlist.RestoreCompleteBottomSheetDialog
|
import org.thoughtcrime.securesms.conversationlist.RestoreCompleteBottomSheetDialog
|
||||||
import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter
|
import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter
|
||||||
|
import org.thoughtcrime.securesms.conversationlist.model.UnreadPaymentsLiveData
|
||||||
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceExitActivity
|
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceExitActivity
|
||||||
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity
|
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity
|
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity
|
||||||
import org.thoughtcrime.securesms.main.MainActivityListHostFragment
|
|
||||||
import org.thoughtcrime.securesms.main.MainBottomChrome
|
import org.thoughtcrime.securesms.main.MainBottomChrome
|
||||||
import org.thoughtcrime.securesms.main.MainBottomChromeCallback
|
import org.thoughtcrime.securesms.main.MainBottomChromeCallback
|
||||||
import org.thoughtcrime.securesms.main.MainBottomChromeState
|
import org.thoughtcrime.securesms.main.MainBottomChromeState
|
||||||
@@ -97,7 +106,9 @@ import org.thoughtcrime.securesms.main.MainNavigationViewModel
|
|||||||
import org.thoughtcrime.securesms.main.MainToolbar
|
import org.thoughtcrime.securesms.main.MainToolbar
|
||||||
import org.thoughtcrime.securesms.main.MainToolbarCallback
|
import org.thoughtcrime.securesms.main.MainToolbarCallback
|
||||||
import org.thoughtcrime.securesms.main.MainToolbarMode
|
import org.thoughtcrime.securesms.main.MainToolbarMode
|
||||||
|
import org.thoughtcrime.securesms.main.MainToolbarState
|
||||||
import org.thoughtcrime.securesms.main.MainToolbarViewModel
|
import org.thoughtcrime.securesms.main.MainToolbarViewModel
|
||||||
|
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder
|
||||||
import org.thoughtcrime.securesms.main.NavigationBarSpacerCompat
|
import org.thoughtcrime.securesms.main.NavigationBarSpacerCompat
|
||||||
import org.thoughtcrime.securesms.main.SnackbarState
|
import org.thoughtcrime.securesms.main.SnackbarState
|
||||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
|
||||||
@@ -107,25 +118,35 @@ import org.thoughtcrime.securesms.megaphone.MegaphoneActionController
|
|||||||
import org.thoughtcrime.securesms.megaphone.Megaphones
|
import org.thoughtcrime.securesms.megaphone.Megaphones
|
||||||
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
|
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
|
||||||
import org.thoughtcrime.securesms.notifications.VitalsViewModel
|
import org.thoughtcrime.securesms.notifications.VitalsViewModel
|
||||||
|
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
|
||||||
|
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfiles
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
import org.thoughtcrime.securesms.profiles.manage.UsernameEditFragment
|
import org.thoughtcrime.securesms.profiles.manage.UsernameEditFragment
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService
|
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
|
import org.thoughtcrime.securesms.stories.landing.StoriesLandingFragment
|
||||||
import org.thoughtcrime.securesms.stories.settings.StorySettingsActivity
|
import org.thoughtcrime.securesms.stories.settings.StorySettingsActivity
|
||||||
import org.thoughtcrime.securesms.util.AppForegroundObserver
|
import org.thoughtcrime.securesms.util.AppForegroundObserver
|
||||||
import org.thoughtcrime.securesms.util.AppStartup
|
import org.thoughtcrime.securesms.util.AppStartup
|
||||||
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||||
import org.thoughtcrime.securesms.util.CachedInflater
|
import org.thoughtcrime.securesms.util.CachedInflater
|
||||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme
|
import org.thoughtcrime.securesms.util.DynamicTheme
|
||||||
|
import org.thoughtcrime.securesms.util.Material3OnScrollHelper
|
||||||
import org.thoughtcrime.securesms.util.SplashScreenUtil
|
import org.thoughtcrime.securesms.util.SplashScreenUtil
|
||||||
|
import org.thoughtcrime.securesms.util.TopToastPopup
|
||||||
|
import org.thoughtcrime.securesms.util.Util
|
||||||
import org.thoughtcrime.securesms.util.viewModel
|
import org.thoughtcrime.securesms.util.viewModel
|
||||||
import org.thoughtcrime.securesms.window.AppScaffold
|
import org.thoughtcrime.securesms.window.AppScaffold
|
||||||
import org.thoughtcrime.securesms.window.WindowSizeClass
|
import org.thoughtcrime.securesms.window.WindowSizeClass
|
||||||
|
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
||||||
|
|
||||||
class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner, MainNavigator.NavigatorProvider {
|
class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner, MainNavigator.NavigatorProvider, Material3OnScrollHelperBinder, ConversationListFragment.Callback, CallLogFragment.Callback {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val TAG = Log.tag(MainActivity::class)
|
||||||
|
|
||||||
private const val KEY_STARTING_TAB = "STARTING_TAB"
|
private const val KEY_STARTING_TAB = "STARTING_TAB"
|
||||||
const val RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901
|
const val RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901
|
||||||
|
|
||||||
@@ -172,6 +193,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
private val motionEventRelay: MotionEventRelay by viewModels()
|
private val motionEventRelay: MotionEventRelay by viewModels()
|
||||||
|
|
||||||
private var onFirstRender = false
|
private var onFirstRender = false
|
||||||
|
private var previousTopToastPopup: TopToastPopup? = null
|
||||||
|
|
||||||
private val mainBottomChromeCallback = BottomChromeCallback()
|
private val mainBottomChromeCallback = BottomChromeCallback()
|
||||||
private val megaphoneActionController = MainMegaphoneActionController()
|
private val megaphoneActionController = MainMegaphoneActionController()
|
||||||
@@ -202,27 +224,49 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
UnreadPaymentsLiveData().observe(this) { unread ->
|
||||||
|
toolbarViewModel.setHasUnreadPayments(unread.isPresent)
|
||||||
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
launch {
|
||||||
mainNavigationViewModel.navigationEvents.collectLatest {
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
when (it) {
|
mainNavigationViewModel.navigationEvents.collectLatest {
|
||||||
MainNavigationViewModel.NavigationEvent.STORY_CAMERA_FIRST -> {
|
when (it) {
|
||||||
mainBottomChromeCallback.onCameraClick(MainNavigationListLocation.STORIES)
|
MainNavigationViewModel.NavigationEvent.STORY_CAMERA_FIRST -> {
|
||||||
|
mainBottomChromeCallback.onCameraClick(MainNavigationListLocation.STORIES)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
launch {
|
||||||
|
mainNavigationViewModel.getNotificationProfiles().collectLatest { profiles ->
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
updateNotificationProfileStatus(profiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shareDataTimestampViewModel.setTimestampFromActivityCreation(savedInstanceState, intent)
|
shareDataTimestampViewModel.setTimestampFromActivityCreation(savedInstanceState, intent)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
val listHostState = rememberFragmentState()
|
|
||||||
val snackbar by mainNavigationViewModel.snackbar.collectAsStateWithLifecycle()
|
val snackbar by mainNavigationViewModel.snackbar.collectAsStateWithLifecycle()
|
||||||
val mainToolbarState by toolbarViewModel.state.collectAsStateWithLifecycle()
|
val mainToolbarState by toolbarViewModel.state.collectAsStateWithLifecycle()
|
||||||
val megaphone by mainNavigationViewModel.megaphone.collectAsStateWithLifecycle()
|
val megaphone by mainNavigationViewModel.megaphone.collectAsStateWithLifecycle()
|
||||||
val mainNavigationState by mainNavigationViewModel.mainNavigationState.collectAsStateWithLifecycle()
|
val mainNavigationState by mainNavigationViewModel.mainNavigationState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
LaunchedEffect(mainNavigationState.selectedDestination) {
|
||||||
|
when (mainNavigationState.selectedDestination) {
|
||||||
|
MainNavigationListLocation.CHATS -> toolbarViewModel.presentToolbarForConversationListFragment()
|
||||||
|
MainNavigationListLocation.ARCHIVE -> toolbarViewModel.presentToolbarForConversationListArchiveFragment()
|
||||||
|
MainNavigationListLocation.CALLS -> toolbarViewModel.presentToolbarForCallLogFragment()
|
||||||
|
MainNavigationListLocation.STORIES -> toolbarViewModel.presentToolbarForStoriesLandingFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isNavigationVisible = remember(mainToolbarState.mode) {
|
val isNavigationVisible = remember(mainToolbarState.mode) {
|
||||||
mainToolbarState.mode == MainToolbarMode.FULL
|
mainToolbarState.mode == MainToolbarMode.FULL
|
||||||
}
|
}
|
||||||
@@ -296,11 +340,40 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
) {
|
) {
|
||||||
AndroidFragment(
|
when (val destination = mainNavigationState.selectedDestination) {
|
||||||
clazz = MainActivityListHostFragment::class.java,
|
MainNavigationListLocation.CHATS -> {
|
||||||
fragmentState = listHostState,
|
val state = key(destination) { rememberFragmentState() }
|
||||||
modifier = Modifier.fillMaxSize()
|
AndroidFragment(
|
||||||
)
|
clazz = ConversationListFragment::class.java,
|
||||||
|
fragmentState = state,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MainNavigationListLocation.ARCHIVE -> {
|
||||||
|
val state = key(destination) { rememberFragmentState() }
|
||||||
|
AndroidFragment(
|
||||||
|
clazz = ConversationListArchiveFragment::class.java,
|
||||||
|
fragmentState = state,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MainNavigationListLocation.CALLS -> {
|
||||||
|
val state = key(destination) { rememberFragmentState() }
|
||||||
|
AndroidFragment(
|
||||||
|
clazz = CallLogFragment::class.java,
|
||||||
|
fragmentState = state,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MainNavigationListLocation.STORIES -> {
|
||||||
|
val state = key(destination) { rememberFragmentState() }
|
||||||
|
AndroidFragment(
|
||||||
|
clazz = StoriesLandingFragment::class.java,
|
||||||
|
fragmentState = state,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MainBottomChrome(
|
MainBottomChrome(
|
||||||
state = mainBottomChromeState,
|
state = mainBottomChromeState,
|
||||||
@@ -433,6 +506,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
|
|
||||||
when (startingTab) {
|
when (startingTab) {
|
||||||
MainNavigationListLocation.CHATS -> mainNavigationViewModel.onChatsSelected()
|
MainNavigationListLocation.CHATS -> mainNavigationViewModel.onChatsSelected()
|
||||||
|
MainNavigationListLocation.ARCHIVE -> mainNavigationViewModel.onArchiveSelected()
|
||||||
MainNavigationListLocation.CALLS -> mainNavigationViewModel.onCallsSelected()
|
MainNavigationListLocation.CALLS -> mainNavigationViewModel.onCallsSelected()
|
||||||
MainNavigationListLocation.STORIES -> {
|
MainNavigationListLocation.STORIES -> {
|
||||||
if (Stories.isFeatureEnabled()) {
|
if (Stories.isFeatureEnabled()) {
|
||||||
@@ -453,6 +527,8 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
dynamicTheme.onResume(this)
|
dynamicTheme.onResume(this)
|
||||||
|
|
||||||
|
toolbarViewModel.refresh()
|
||||||
|
|
||||||
if (SignalStore.misc.shouldShowLinkedDevicesReminder) {
|
if (SignalStore.misc.shouldShowLinkedDevicesReminder) {
|
||||||
SignalStore.misc.shouldShowLinkedDevicesReminder = false
|
SignalStore.misc.shouldShowLinkedDevicesReminder = false
|
||||||
RelinkDevicesReminderBottomSheetFragment.show(supportFragmentManager)
|
RelinkDevicesReminderBottomSheetFragment.show(supportFragmentManager)
|
||||||
@@ -516,6 +592,56 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
return navigator
|
return navigator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun bindScrollHelper(recyclerView: RecyclerView, lifecycleOwner: LifecycleOwner) {
|
||||||
|
Material3OnScrollHelper(
|
||||||
|
activity = this,
|
||||||
|
views = listOf(),
|
||||||
|
viewStubs = listOf(),
|
||||||
|
onSetToolbarColor = {
|
||||||
|
toolbarViewModel.setToolbarColor(it)
|
||||||
|
},
|
||||||
|
setStatusBarColor = {},
|
||||||
|
lifecycleOwner = lifecycleOwner
|
||||||
|
).attach(recyclerView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindScrollHelper(recyclerView: RecyclerView, lifecycleOwner: LifecycleOwner, chatFolders: RecyclerView, setChatFolder: (Int) -> Unit) {
|
||||||
|
Material3OnScrollHelper(
|
||||||
|
activity = this,
|
||||||
|
views = listOf(chatFolders),
|
||||||
|
viewStubs = listOf(),
|
||||||
|
setStatusBarColor = {},
|
||||||
|
onSetToolbarColor = {
|
||||||
|
toolbarViewModel.setToolbarColor(it)
|
||||||
|
},
|
||||||
|
lifecycleOwner = lifecycleOwner,
|
||||||
|
setChatFolderColor = setChatFolder
|
||||||
|
).attach(recyclerView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateProxyStatus(state: WebSocketConnectionState) {
|
||||||
|
if (SignalStore.proxy.isProxyEnabled) {
|
||||||
|
val proxyState: MainToolbarState.ProxyState = when (state) {
|
||||||
|
WebSocketConnectionState.CONNECTING, WebSocketConnectionState.DISCONNECTING, WebSocketConnectionState.DISCONNECTED -> MainToolbarState.ProxyState.CONNECTING
|
||||||
|
WebSocketConnectionState.CONNECTED -> MainToolbarState.ProxyState.CONNECTED
|
||||||
|
WebSocketConnectionState.AUTHENTICATION_FAILED, WebSocketConnectionState.FAILED, WebSocketConnectionState.REMOTE_DEPRECATED -> MainToolbarState.ProxyState.FAILED
|
||||||
|
else -> MainToolbarState.ProxyState.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbarViewModel.setProxyState(proxyState = proxyState)
|
||||||
|
} else {
|
||||||
|
toolbarViewModel.setProxyState(proxyState = MainToolbarState.ProxyState.NONE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMultiSelectStarted() {
|
||||||
|
toolbarViewModel.presentToolbarForMultiselect()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMultiSelectFinished() {
|
||||||
|
toolbarViewModel.presentToolbarForCurrentDestination()
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleDeepLinkIntent(intent: Intent) {
|
private fun handleDeepLinkIntent(intent: Intent) {
|
||||||
handleConversationIntent(intent)
|
handleConversationIntent(intent)
|
||||||
handleGroupLinkInIntent(intent)
|
handleGroupLinkInIntent(intent)
|
||||||
@@ -578,6 +704,44 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateNotificationProfileStatus(notificationProfiles: List<NotificationProfile>) {
|
||||||
|
val activeProfile = NotificationProfiles.getActiveProfile(notificationProfiles)
|
||||||
|
if (activeProfile != null) {
|
||||||
|
if (activeProfile.id != SignalStore.notificationProfile.lastProfilePopup) {
|
||||||
|
val view = findViewById<ViewGroup>(android.R.id.content)
|
||||||
|
|
||||||
|
view.postDelayed({
|
||||||
|
try {
|
||||||
|
var fragmentView = view ?: return@postDelayed
|
||||||
|
|
||||||
|
SignalStore.notificationProfile.lastProfilePopup = activeProfile.id
|
||||||
|
SignalStore.notificationProfile.lastProfilePopupTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
if (previousTopToastPopup?.isShowing == true) {
|
||||||
|
previousTopToastPopup?.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
val fragment = supportFragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
||||||
|
if (fragment != null && fragment.isAdded && fragment.view != null) {
|
||||||
|
fragmentView = fragment.requireView() as ViewGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
previousTopToastPopup = TopToastPopup.show(fragmentView, R.drawable.ic_moon_16, getString(R.string.ConversationListFragment__s_on, activeProfile.name))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Unable to show toast popup", e)
|
||||||
|
}
|
||||||
|
}, 500L)
|
||||||
|
}
|
||||||
|
toolbarViewModel.setNotificationProfileEnabled(true)
|
||||||
|
} else {
|
||||||
|
toolbarViewModel.setNotificationProfileEnabled(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SignalStore.notificationProfile.hasSeenTooltip && Util.hasItems(notificationProfiles)) {
|
||||||
|
toolbarViewModel.setShowNotificationProfilesTooltip(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inner class ToolbarCallback : MainToolbarCallback {
|
inner class ToolbarCallback : MainToolbarCallback {
|
||||||
|
|
||||||
override fun onNewGroupClick() {
|
override fun onNewGroupClick() {
|
||||||
@@ -744,6 +908,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
MainNavigationListLocation.CHATS -> mainNavigationViewModel.onChatsSelected()
|
MainNavigationListLocation.CHATS -> mainNavigationViewModel.onChatsSelected()
|
||||||
MainNavigationListLocation.CALLS -> mainNavigationViewModel.onCallsSelected()
|
MainNavigationListLocation.CALLS -> mainNavigationViewModel.onCallsSelected()
|
||||||
MainNavigationListLocation.STORIES -> mainNavigationViewModel.onStoriesSelected()
|
MainNavigationListLocation.STORIES -> mainNavigationViewModel.onStoriesSelected()
|
||||||
|
MainNavigationListLocation.ARCHIVE -> mainNavigationViewModel.onArchiveSelected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,18 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalInspectionMode
|
import androidx.compose.ui.platform.LocalInspectionMode
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.lifecycle.map
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.rx3.asFlow
|
||||||
import org.thoughtcrime.securesms.components.AvatarImageView
|
import org.thoughtcrime.securesms.components.AvatarImageView
|
||||||
import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails
|
import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper
|
import org.thoughtcrime.securesms.profiles.AvatarHelper
|
||||||
@@ -36,11 +41,21 @@ fun AvatarImage(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val state = recipient.live().liveData.map { AvatarImageState(NameUtil.getAbbreviation(it.getDisplayName(context)), it, AvatarHelper.getAvatarFileDetails(context, it.id)) }.observeAsState().value ?: return
|
var state: AvatarImageState by remember {
|
||||||
|
mutableStateOf(AvatarImageState(null, recipient, ProfileAvatarFileDetails.NO_DETAILS))
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(recipient.id) {
|
||||||
|
Recipient.observable(recipient.id).asFlow()
|
||||||
|
.collectLatest {
|
||||||
|
state = AvatarImageState(NameUtil.getAbbreviation(it.getDisplayName(context)), it, AvatarHelper.getAvatarFileDetails(context, it.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = {
|
factory = {
|
||||||
AvatarImageView(context).apply {
|
AvatarImageView(context).apply {
|
||||||
|
initialize(context, null)
|
||||||
this.contentDescription = contentDescription
|
this.contentDescription = contentDescription
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.setFragmentResult
|
import androidx.fragment.app.setFragmentResult
|
||||||
import androidx.navigation.fragment.navArgs
|
|
||||||
import org.signal.core.ui.compose.Buttons
|
import org.signal.core.ui.compose.Buttons
|
||||||
import org.signal.core.ui.compose.Scaffolds
|
import org.signal.core.ui.compose.Scaffolds
|
||||||
import org.signal.core.util.BreakIteratorCompat
|
import org.signal.core.util.BreakIteratorCompat
|
||||||
@@ -45,11 +44,11 @@ class EditCallLinkNameDialogFragment : ComposeDialogFragment() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val RESULT_KEY = "edit_call_link_name"
|
const val RESULT_KEY = "edit_call_link_name"
|
||||||
|
const val ARG_NAME = "name"
|
||||||
private const val MAX_CHARACTER_COUNT = 32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val args: EditCallLinkNameDialogFragmentArgs by navArgs()
|
private val argName: String
|
||||||
|
get() = requireArguments().getString(ARG_NAME)!!
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -69,8 +68,8 @@ class EditCallLinkNameDialogFragment : ComposeDialogFragment() {
|
|||||||
var callName by remember {
|
var callName by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
TextFieldValue(
|
TextFieldValue(
|
||||||
text = args.name,
|
text = argName,
|
||||||
selection = TextRange(args.name.length)
|
selection = TextRange(argName.length)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ import androidx.compose.ui.res.vectorResource
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.app.ShareCompat
|
import androidx.core.app.ShareCompat
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||||
import org.signal.core.ui.compose.BottomSheets
|
import org.signal.core.ui.compose.BottomSheets
|
||||||
import org.signal.core.ui.compose.Buttons
|
import org.signal.core.ui.compose.Buttons
|
||||||
@@ -127,9 +127,9 @@ class CreateCallLinkBottomSheetDialogFragment : ComposeBottomSheetDialogFragment
|
|||||||
|
|
||||||
private fun onAddACallNameClicked() {
|
private fun onAddACallNameClicked() {
|
||||||
val snapshot = viewModel.callLink.value
|
val snapshot = viewModel.callLink.value
|
||||||
findNavController().navigate(
|
EditCallLinkNameDialogFragment().apply {
|
||||||
CreateCallLinkBottomSheetDialogFragmentDirections.actionCreateCallLinkBottomSheetToEditCallLinkNameDialogFragment(snapshot.state.name)
|
arguments = bundleOf(EditCallLinkNameDialogFragment.ARG_NAME to snapshot.state.name)
|
||||||
)
|
}.show(parentFragmentManager, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onJoinClicked() {
|
private fun onJoinClicked() {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import androidx.compose.material3.SnackbarDuration
|
|||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
@@ -26,6 +25,7 @@ import org.signal.core.util.concurrent.addTo
|
|||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
import org.thoughtcrime.securesms.MainNavigator
|
import org.thoughtcrime.securesms.MainNavigator
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.calls.links.create.CreateCallLinkBottomSheetDialogFragment
|
||||||
import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsActivity
|
import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsActivity
|
||||||
import org.thoughtcrime.securesms.components.ProgressCardDialogFragment
|
import org.thoughtcrime.securesms.components.ProgressCardDialogFragment
|
||||||
import org.thoughtcrime.securesms.components.ScrollToPositionDelegate
|
import org.thoughtcrime.securesms.components.ScrollToPositionDelegate
|
||||||
@@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.main.MainToolbarViewModel
|
|||||||
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder
|
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder
|
||||||
import org.thoughtcrime.securesms.main.SnackbarState
|
import org.thoughtcrime.securesms.main.SnackbarState
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
import org.thoughtcrime.securesms.util.doAfterNextLayout
|
import org.thoughtcrime.securesms.util.doAfterNextLayout
|
||||||
@@ -293,7 +294,7 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateACallLinkClicked() {
|
override fun onCreateACallLinkClicked() {
|
||||||
findNavController().navigate(R.id.createCallLinkBottomSheet)
|
CreateCallLinkBottomSheetDialogFragment().show(parentFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallClicked(callLogRow: CallLogRow.Call) {
|
override fun onCallClicked(callLogRow: CallLogRow.Call) {
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import android.content.ActivityNotFoundException
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.app.ShareCompat
|
import androidx.core.app.ShareCompat
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.thoughtcrime.securesms.BaseActivity
|
import org.thoughtcrime.securesms.BaseActivity
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.calls.links.CallLinks
|
import org.thoughtcrime.securesms.calls.links.CallLinks
|
||||||
import org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragment
|
import org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragment
|
||||||
import org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragmentArgs
|
|
||||||
import org.thoughtcrime.securesms.components.webrtc.controls.CallInfoView
|
import org.thoughtcrime.securesms.components.webrtc.controls.CallInfoView
|
||||||
import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel
|
import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel
|
||||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||||
@@ -44,7 +44,7 @@ class CallInfoCallbacks(
|
|||||||
|
|
||||||
override fun onEditNameClicked(name: String) {
|
override fun onEditNameClicked(name: String) {
|
||||||
EditCallLinkNameDialogFragment().apply {
|
EditCallLinkNameDialogFragment().apply {
|
||||||
arguments = EditCallLinkNameDialogFragmentArgs.Builder(name).build().toBundle()
|
arguments = bundleOf(EditCallLinkNameDialogFragment.ARG_NAME to name)
|
||||||
}.show(activity.supportFragmentManager, null)
|
}.show(activity.supportFragmentManager, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -937,6 +937,7 @@ class ConversationFragment :
|
|||||||
firstRender = false
|
firstRender = false
|
||||||
binding.conversationItemRecycler.doAfterNextLayout {
|
binding.conversationItemRecycler.doAfterNextLayout {
|
||||||
SignalLocalMetrics.ConversationOpen.onRenderFinished()
|
SignalLocalMetrics.ConversationOpen.onRenderFinished()
|
||||||
|
(requireActivity() as? MainActivity)?.onFirstRender()
|
||||||
doAfterFirstRender()
|
doAfterFirstRender()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import androidx.compose.ui.res.vectorResource
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import org.signal.core.ui.compose.BottomSheets
|
import org.signal.core.ui.compose.BottomSheets
|
||||||
import org.signal.core.ui.compose.Previews
|
import org.signal.core.ui.compose.Previews
|
||||||
import org.signal.core.ui.compose.SignalPreview
|
import org.signal.core.ui.compose.SignalPreview
|
||||||
@@ -59,7 +59,7 @@ class AddToFolderBottomSheet private constructor(private val onDismissListener:
|
|||||||
OTHER(3)
|
OTHER(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val viewModel: ConversationListViewModel by viewModels(
|
private val viewModel: ConversationListViewModel.UnarchivedConversationListViewModel by activityViewModels(
|
||||||
factoryProducer = {
|
factoryProducer = {
|
||||||
ConversationListViewModel.Factory(isArchived = false)
|
ConversationListViewModel.Factory(isArchived = false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.Vie
|
|||||||
this.onConversationClickListener = onConversationClickListener;
|
this.onConversationClickListener = onConversationClickListener;
|
||||||
this.onClearFilterClicked = onClearFilterClicked;
|
this.onClearFilterClicked = onClearFilterClicked;
|
||||||
this.onFolderSettingsClicked = onFolderSettingsClicked;
|
this.onFolderSettingsClicked = onFolderSettingsClicked;
|
||||||
|
|
||||||
|
setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
|||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
@@ -643,8 +642,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
@Override
|
@Override
|
||||||
public void onShowArchiveClick() {
|
public void onShowArchiveClick() {
|
||||||
if (viewModel.currentSelectedConversations().isEmpty()) {
|
if (viewModel.currentSelectedConversations().isEmpty()) {
|
||||||
NavHostFragment.findNavController(this)
|
mainNavigationViewModel.goTo(MainNavigationListLocation.ARCHIVE);
|
||||||
.navigate(ConversationListFragmentDirections.actionConversationListFragmentToConversationListArchiveFragment());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,7 +705,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
} else if (event instanceof MainToolbarViewModel.Event.Chats.ClearFilter) {
|
} else if (event instanceof MainToolbarViewModel.Event.Chats.ClearFilter) {
|
||||||
onClearFilterClick();
|
onClearFilterClick();
|
||||||
} else if (event instanceof MainToolbarViewModel.Event.Chats.CloseArchive) {
|
} else if (event instanceof MainToolbarViewModel.Event.Chats.CloseArchive) {
|
||||||
NavHostFragment.findNavController(this).popBackStack(R.id.conversationListFragment, false);
|
mainNavigationViewModel.goTo(MainNavigationListLocation.CHATS);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -855,7 +853,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeViewModel() {
|
private void initializeViewModel() {
|
||||||
viewModel = new ViewModelProvider(this, new ConversationListViewModel.Factory(isArchived())).get(ConversationListViewModel.class);
|
Class<? extends ConversationListViewModel> viewModelClass = isArchived() ? ConversationListViewModel.ArchivedConversationListViewModel.class : ConversationListViewModel.UnarchivedConversationListViewModel.class;
|
||||||
|
viewModel = new ViewModelProvider(requireActivity(), new ConversationListViewModel.Factory(isArchived())).get(viewModelClass);
|
||||||
|
|
||||||
lifecycleDisposable.add(viewModel.getConversationsState().subscribe(this::onConversationListChanged));
|
lifecycleDisposable.add(viewModel.getConversationsState().subscribe(this::onConversationListChanged));
|
||||||
lifecycleDisposable.add(viewModel.getHasNoConversations().subscribe(this::updateEmptyState));
|
lifecycleDisposable.add(viewModel.getHasNoConversations().subscribe(this::updateEmptyState));
|
||||||
@@ -1389,7 +1388,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Callback requireCallback() {
|
protected Callback requireCallback() {
|
||||||
return ((Callback) getParentFragment().getParentFragment());
|
return ((Callback) requireActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected @PluralsRes int getArchivedSnackbarTitleRes() {
|
protected @PluralsRes int getArchivedSnackbarTitleRes() {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import org.thoughtcrime.securesms.util.rx.RxStore
|
|||||||
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class ConversationListViewModel(
|
sealed class ConversationListViewModel(
|
||||||
private val isArchived: Boolean,
|
private val isArchived: Boolean,
|
||||||
private val savedStateHandle: SavedStateHandle
|
private val savedStateHandle: SavedStateHandle
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@@ -317,13 +317,20 @@ class ConversationListViewModel(
|
|||||||
val pinnedCount: Int = 0
|
val pinnedCount: Int = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class UnarchivedConversationListViewModel(savedStateHandle: SavedStateHandle) : ConversationListViewModel(isArchived = false, savedStateHandle = savedStateHandle)
|
||||||
|
class ArchivedConversationListViewModel(savedStateHandle: SavedStateHandle) : ConversationListViewModel(isArchived = true, savedStateHandle = savedStateHandle)
|
||||||
|
|
||||||
class Factory(
|
class Factory(
|
||||||
private val isArchived: Boolean
|
private val isArchived: Boolean
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
||||||
val savedStateHandle = extras.createSavedStateHandle()
|
val savedStateHandle = extras.createSavedStateHandle()
|
||||||
|
|
||||||
return modelClass.cast(ConversationListViewModel(isArchived, savedStateHandle))!!
|
return if (isArchived) {
|
||||||
|
ArchivedConversationListViewModel(savedStateHandle) as T
|
||||||
|
} else {
|
||||||
|
UnarchivedConversationListViewModel(savedStateHandle) as T
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,282 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.main
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavDestination
|
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
|
||||||
import org.signal.core.util.logging.Log
|
|
||||||
import org.thoughtcrime.securesms.R
|
|
||||||
import org.thoughtcrime.securesms.calls.log.CallLogFragment
|
|
||||||
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment
|
|
||||||
import org.thoughtcrime.securesms.conversationlist.model.UnreadPaymentsLiveData
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
|
||||||
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
|
|
||||||
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfiles
|
|
||||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
|
||||||
import org.thoughtcrime.securesms.util.Material3OnScrollHelper
|
|
||||||
import org.thoughtcrime.securesms.util.TopToastPopup
|
|
||||||
import org.thoughtcrime.securesms.util.Util
|
|
||||||
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
|
|
||||||
|
|
||||||
class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_fragment), ConversationListFragment.Callback, Material3OnScrollHelperBinder, CallLogFragment.Callback {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val TAG = Log.tag(MainActivityListHostFragment::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val disposables: LifecycleDisposable = LifecycleDisposable()
|
|
||||||
|
|
||||||
private var previousTopToastPopup: TopToastPopup? = null
|
|
||||||
|
|
||||||
private val destinationChangedListener = DestinationChangedListener()
|
|
||||||
private val toolbarViewModel: MainToolbarViewModel by activityViewModels()
|
|
||||||
private val mainNavigationViewModel: MainNavigationViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
disposables.bindTo(viewLifecycleOwner)
|
|
||||||
|
|
||||||
UnreadPaymentsLiveData().observe(viewLifecycleOwner) { unread ->
|
|
||||||
toolbarViewModel.setHasUnreadPayments(unread.isPresent)
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
launch {
|
|
||||||
mainNavigationViewModel.mainNavigationState.collectLatest { state ->
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
val controller: NavController = getChildNavController()
|
|
||||||
when (controller.currentDestination?.id) {
|
|
||||||
R.id.conversationListFragment -> goToStateFromConversationList(state, controller)
|
|
||||||
R.id.conversationListArchiveFragment -> Unit
|
|
||||||
R.id.storiesLandingFragment -> goToStateFromStories(state, controller)
|
|
||||||
R.id.callLogFragment -> goToStateFromCalling(state, controller)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
launch {
|
|
||||||
mainNavigationViewModel.getNotificationProfiles().collectLatest { profiles ->
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
updateNotificationProfileStatus(profiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getChildNavController(): NavController {
|
|
||||||
return requireView().findViewById<View>(R.id.fragment_container).findNavController()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun goToStateFromConversationList(state: MainNavigationState, navController: NavController) {
|
|
||||||
if (state.selectedDestination == MainNavigationListLocation.CHATS) {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
val destination = if (state.selectedDestination == MainNavigationListLocation.STORIES) {
|
|
||||||
R.id.action_conversationListFragment_to_storiesLandingFragment
|
|
||||||
} else {
|
|
||||||
R.id.action_conversationListFragment_to_callLogFragment
|
|
||||||
}
|
|
||||||
|
|
||||||
navController.navigate(
|
|
||||||
destination,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun goToStateFromCalling(state: MainNavigationState, navController: NavController) {
|
|
||||||
when (state.selectedDestination) {
|
|
||||||
MainNavigationListLocation.CALLS -> return
|
|
||||||
MainNavigationListLocation.CHATS -> navController.popBackStack(R.id.conversationListFragment, false)
|
|
||||||
MainNavigationListLocation.STORIES -> navController.navigate(R.id.action_callLogFragment_to_storiesLandingFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun goToStateFromStories(state: MainNavigationState, navController: NavController) {
|
|
||||||
when (state.selectedDestination) {
|
|
||||||
MainNavigationListLocation.STORIES -> return
|
|
||||||
MainNavigationListLocation.CHATS -> navController.popBackStack(R.id.conversationListFragment, false)
|
|
||||||
MainNavigationListLocation.CALLS -> navController.navigate(R.id.action_storiesLandingFragment_to_callLogFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
toolbarViewModel.refresh()
|
|
||||||
|
|
||||||
requireView()
|
|
||||||
.findViewById<View>(R.id.fragment_container)
|
|
||||||
.findNavController()
|
|
||||||
.addOnDestinationChangedListener(destinationChangedListener)
|
|
||||||
|
|
||||||
if (toolbarViewModel.state.value.mode == MainToolbarMode.ACTION_MODE) {
|
|
||||||
presentToolbarForMultiselect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
requireView()
|
|
||||||
.findViewById<View>(R.id.fragment_container)
|
|
||||||
.findNavController()
|
|
||||||
.removeOnDestinationChangedListener(destinationChangedListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun presentToolbarForConversationListFragment() {
|
|
||||||
toolbarViewModel.setToolbarMode(MainToolbarMode.FULL, destination = MainNavigationListLocation.CHATS, overwriteSearchMode = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun presentToolbarForConversationListArchiveFragment() {
|
|
||||||
toolbarViewModel.setToolbarMode(MainToolbarMode.BASIC, destination = MainNavigationListLocation.CHATS)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun presentToolbarForStoriesLandingFragment() {
|
|
||||||
toolbarViewModel.setToolbarMode(MainToolbarMode.FULL, destination = MainNavigationListLocation.STORIES)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun presentToolbarForCallLogFragment() {
|
|
||||||
toolbarViewModel.setToolbarMode(MainToolbarMode.FULL, destination = MainNavigationListLocation.CALLS)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun presentToolbarForMultiselect() {
|
|
||||||
toolbarViewModel.setToolbarMode(MainToolbarMode.ACTION_MODE)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
previousTopToastPopup = null
|
|
||||||
super.onDestroyView()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMultiSelectStarted() {
|
|
||||||
presentToolbarForMultiselect()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMultiSelectFinished() {
|
|
||||||
val currentDestination: NavDestination? = requireView().findViewById<View>(R.id.fragment_container).findNavController().currentDestination
|
|
||||||
if (currentDestination != null) {
|
|
||||||
presentToolbarForDestination(currentDestination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateProxyStatus(state: WebSocketConnectionState) {
|
|
||||||
if (SignalStore.proxy.isProxyEnabled) {
|
|
||||||
val proxyState: MainToolbarState.ProxyState = when (state) {
|
|
||||||
WebSocketConnectionState.CONNECTING, WebSocketConnectionState.DISCONNECTING, WebSocketConnectionState.DISCONNECTED -> MainToolbarState.ProxyState.CONNECTING
|
|
||||||
WebSocketConnectionState.CONNECTED -> MainToolbarState.ProxyState.CONNECTED
|
|
||||||
WebSocketConnectionState.AUTHENTICATION_FAILED, WebSocketConnectionState.FAILED, WebSocketConnectionState.REMOTE_DEPRECATED -> MainToolbarState.ProxyState.FAILED
|
|
||||||
else -> MainToolbarState.ProxyState.NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbarViewModel.setProxyState(proxyState = proxyState)
|
|
||||||
} else {
|
|
||||||
toolbarViewModel.setProxyState(proxyState = MainToolbarState.ProxyState.NONE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateNotificationProfileStatus(notificationProfiles: List<NotificationProfile>) {
|
|
||||||
val activeProfile = NotificationProfiles.getActiveProfile(notificationProfiles)
|
|
||||||
if (activeProfile != null) {
|
|
||||||
if (activeProfile.id != SignalStore.notificationProfile.lastProfilePopup) {
|
|
||||||
view?.postDelayed({
|
|
||||||
try {
|
|
||||||
var fragmentView = view as? ViewGroup ?: return@postDelayed
|
|
||||||
|
|
||||||
SignalStore.notificationProfile.lastProfilePopup = activeProfile.id
|
|
||||||
SignalStore.notificationProfile.lastProfilePopupTime = System.currentTimeMillis()
|
|
||||||
|
|
||||||
if (previousTopToastPopup?.isShowing == true) {
|
|
||||||
previousTopToastPopup?.dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
val fragment = parentFragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
|
||||||
if (fragment != null && fragment.isAdded && fragment.view != null) {
|
|
||||||
fragmentView = fragment.requireView() as ViewGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
previousTopToastPopup = TopToastPopup.show(fragmentView, R.drawable.ic_moon_16, getString(R.string.ConversationListFragment__s_on, activeProfile.name))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Unable to show toast popup", e)
|
|
||||||
}
|
|
||||||
}, 500L)
|
|
||||||
}
|
|
||||||
toolbarViewModel.setNotificationProfileEnabled(true)
|
|
||||||
} else {
|
|
||||||
toolbarViewModel.setNotificationProfileEnabled(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SignalStore.notificationProfile.hasSeenTooltip && Util.hasItems(notificationProfiles)) {
|
|
||||||
toolbarViewModel.setShowNotificationProfilesTooltip(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun presentToolbarForDestination(destination: NavDestination) {
|
|
||||||
when (destination.id) {
|
|
||||||
R.id.conversationListFragment -> {
|
|
||||||
presentToolbarForConversationListFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.conversationListArchiveFragment -> {
|
|
||||||
presentToolbarForConversationListArchiveFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.storiesLandingFragment -> {
|
|
||||||
presentToolbarForStoriesLandingFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.callLogFragment -> {
|
|
||||||
presentToolbarForCallLogFragment()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class DestinationChangedListener : NavController.OnDestinationChangedListener {
|
|
||||||
override fun onDestinationChanged(controller: NavController, destination: NavDestination, arguments: Bundle?) {
|
|
||||||
presentToolbarForDestination(destination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bindScrollHelper(recyclerView: RecyclerView, lifecycleOwner: LifecycleOwner) {
|
|
||||||
Material3OnScrollHelper(
|
|
||||||
activity = requireActivity(),
|
|
||||||
views = listOf(),
|
|
||||||
viewStubs = listOf(),
|
|
||||||
onSetToolbarColor = {
|
|
||||||
toolbarViewModel.setToolbarColor(it)
|
|
||||||
},
|
|
||||||
setStatusBarColor = {},
|
|
||||||
lifecycleOwner = lifecycleOwner
|
|
||||||
).attach(recyclerView)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bindScrollHelper(recyclerView: RecyclerView, lifecycleOwner: LifecycleOwner, chatFolders: RecyclerView, setChatFolder: (Int) -> Unit) {
|
|
||||||
Material3OnScrollHelper(
|
|
||||||
activity = requireActivity(),
|
|
||||||
views = listOf(chatFolders),
|
|
||||||
viewStubs = listOf(),
|
|
||||||
setStatusBarColor = {},
|
|
||||||
onSetToolbarColor = {
|
|
||||||
toolbarViewModel.setToolbarColor(it)
|
|
||||||
},
|
|
||||||
lifecycleOwner = lifecycleOwner,
|
|
||||||
setChatFolderColor = setChatFolder
|
|
||||||
).attach(recyclerView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -170,6 +170,7 @@ private fun PrimaryActionButton(
|
|||||||
) {
|
) {
|
||||||
val onClick = remember(destination) {
|
val onClick = remember(destination) {
|
||||||
when (destination) {
|
when (destination) {
|
||||||
|
MainNavigationListLocation.ARCHIVE -> error("Not supported")
|
||||||
MainNavigationListLocation.CHATS -> onNewChatClick
|
MainNavigationListLocation.CHATS -> onNewChatClick
|
||||||
MainNavigationListLocation.CALLS -> onNewCallClick
|
MainNavigationListLocation.CALLS -> onNewCallClick
|
||||||
MainNavigationListLocation.STORIES -> {
|
MainNavigationListLocation.STORIES -> {
|
||||||
@@ -184,6 +185,7 @@ private fun PrimaryActionButton(
|
|||||||
icon = {
|
icon = {
|
||||||
AnimatedContent(destination) { targetState ->
|
AnimatedContent(destination) { targetState ->
|
||||||
val (icon, contentDescriptionId) = when (targetState) {
|
val (icon, contentDescriptionId) = when (targetState) {
|
||||||
|
MainNavigationListLocation.ARCHIVE -> error("Not supported")
|
||||||
MainNavigationListLocation.CHATS -> R.drawable.symbol_edit_24 to R.string.conversation_list_fragment__fab_content_description
|
MainNavigationListLocation.CHATS -> R.drawable.symbol_edit_24 to R.string.conversation_list_fragment__fab_content_description
|
||||||
MainNavigationListLocation.CALLS -> R.drawable.symbol_phone_plus_24 to R.string.CallLogFragment__start_a_new_call
|
MainNavigationListLocation.CALLS -> R.drawable.symbol_phone_plus_24 to R.string.CallLogFragment__start_a_new_call
|
||||||
MainNavigationListLocation.STORIES -> R.drawable.symbol_camera_24 to R.string.conversation_list_fragment__open_camera_description
|
MainNavigationListLocation.STORIES -> R.drawable.symbol_camera_24 to R.string.conversation_list_fragment__open_camera_description
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ enum class MainNavigationListLocation(
|
|||||||
label = R.string.ConversationListTabs__chats,
|
label = R.string.ConversationListTabs__chats,
|
||||||
icon = R.raw.chats_28
|
icon = R.raw.chats_28
|
||||||
),
|
),
|
||||||
|
ARCHIVE(
|
||||||
|
label = R.string.ConversationListTabs__chats,
|
||||||
|
icon = R.raw.chats_28
|
||||||
|
),
|
||||||
CALLS(
|
CALLS(
|
||||||
label = R.string.ConversationListTabs__calls,
|
label = R.string.ConversationListTabs__calls,
|
||||||
icon = R.raw.calls_28
|
icon = R.raw.calls_28
|
||||||
@@ -104,15 +108,16 @@ fun MainNavigationBar(
|
|||||||
) {
|
) {
|
||||||
val entries = remember(state.isStoriesFeatureEnabled) {
|
val entries = remember(state.isStoriesFeatureEnabled) {
|
||||||
if (state.isStoriesFeatureEnabled) {
|
if (state.isStoriesFeatureEnabled) {
|
||||||
MainNavigationListLocation.entries
|
MainNavigationListLocation.entries.filterNot { it == MainNavigationListLocation.ARCHIVE }
|
||||||
} else {
|
} else {
|
||||||
MainNavigationListLocation.entries.filterNot { it == MainNavigationListLocation.STORIES }
|
MainNavigationListLocation.entries.filterNot { it == MainNavigationListLocation.STORIES || it == MainNavigationListLocation.ARCHIVE }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.forEach { destination ->
|
entries.forEach { destination ->
|
||||||
|
|
||||||
val badgeCount = when (destination) {
|
val badgeCount = when (destination) {
|
||||||
|
MainNavigationListLocation.ARCHIVE -> error("Not supported")
|
||||||
MainNavigationListLocation.CHATS -> state.chatsCount
|
MainNavigationListLocation.CHATS -> state.chatsCount
|
||||||
MainNavigationListLocation.CALLS -> state.callsCount
|
MainNavigationListLocation.CALLS -> state.callsCount
|
||||||
MainNavigationListLocation.STORIES -> state.storiesCount
|
MainNavigationListLocation.STORIES -> state.storiesCount
|
||||||
@@ -219,9 +224,9 @@ fun MainNavigationRail(
|
|||||||
) {
|
) {
|
||||||
val entries = remember(state.isStoriesFeatureEnabled) {
|
val entries = remember(state.isStoriesFeatureEnabled) {
|
||||||
if (state.isStoriesFeatureEnabled) {
|
if (state.isStoriesFeatureEnabled) {
|
||||||
MainNavigationListLocation.entries
|
MainNavigationListLocation.entries.filterNot { it == MainNavigationListLocation.ARCHIVE }
|
||||||
} else {
|
} else {
|
||||||
MainNavigationListLocation.entries.filterNot { it == MainNavigationListLocation.STORIES }
|
MainNavigationListLocation.entries.filterNot { it == MainNavigationListLocation.STORIES || it == MainNavigationListLocation.ARCHIVE }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +267,7 @@ private fun BoxScope.NavigationRailCountIndicator(
|
|||||||
) {
|
) {
|
||||||
val count = remember(state, destination) {
|
val count = remember(state, destination) {
|
||||||
when (destination) {
|
when (destination) {
|
||||||
|
MainNavigationListLocation.ARCHIVE -> error("Not supported")
|
||||||
MainNavigationListLocation.CHATS -> state.chatsCount
|
MainNavigationListLocation.CHATS -> state.chatsCount
|
||||||
MainNavigationListLocation.CALLS -> state.callsCount
|
MainNavigationListLocation.CALLS -> state.callsCount
|
||||||
MainNavigationListLocation.STORIES -> state.storiesCount
|
MainNavigationListLocation.STORIES -> state.storiesCount
|
||||||
|
|||||||
@@ -95,6 +95,15 @@ class MainNavigationViewModel(
|
|||||||
val wrapped = LegacyNavigator(composeScope, threePaneScaffoldNavigator, goToLegacyDetailLocation)
|
val wrapped = LegacyNavigator(composeScope, threePaneScaffoldNavigator, goToLegacyDetailLocation)
|
||||||
this.navigator = wrapped
|
this.navigator = wrapped
|
||||||
|
|
||||||
|
if (previous != null) {
|
||||||
|
val destination = previous.currentDestination?.contentKey ?: return wrapped
|
||||||
|
if (destination is MainNavigationListLocation) {
|
||||||
|
goTo(destination)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goTo(mainNavigationState.value.selectedDestination)
|
||||||
|
}
|
||||||
|
|
||||||
if (previous != null) {
|
if (previous != null) {
|
||||||
val destination = previous.currentDestination?.contentKey ?: return wrapped
|
val destination = previous.currentDestination?.contentKey ?: return wrapped
|
||||||
if (destination is MainNavigationDetailLocation) {
|
if (destination is MainNavigationDetailLocation) {
|
||||||
@@ -123,6 +132,12 @@ class MainNavigationViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun goTo(location: MainNavigationListLocation) {
|
||||||
|
internalMainNavigationState.update {
|
||||||
|
it.copy(selectedDestination = location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun goToCameraFirstStoryCapture() {
|
fun goToCameraFirstStoryCapture() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
internalNavigationEvents.emit(NavigationEvent.STORY_CAMERA_FIRST)
|
internalNavigationEvents.emit(NavigationEvent.STORY_CAMERA_FIRST)
|
||||||
@@ -168,6 +183,13 @@ class MainNavigationViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onArchiveSelected() {
|
||||||
|
internalTabClickEvents.tryEmit(MainNavigationListLocation.ARCHIVE)
|
||||||
|
internalMainNavigationState.update {
|
||||||
|
it.copy(selectedDestination = MainNavigationListLocation.ARCHIVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onCallsSelected() {
|
fun onCallsSelected() {
|
||||||
internalTabClickEvents.tryEmit(MainNavigationListLocation.CALLS)
|
internalTabClickEvents.tryEmit(MainNavigationListLocation.CALLS)
|
||||||
internalMainNavigationState.update {
|
internalMainNavigationState.update {
|
||||||
|
|||||||
@@ -395,6 +395,7 @@ private fun PrimaryToolbar(
|
|||||||
controller = controller
|
controller = controller
|
||||||
) {
|
) {
|
||||||
when (state.destination) {
|
when (state.destination) {
|
||||||
|
MainNavigationListLocation.ARCHIVE -> Unit
|
||||||
MainNavigationListLocation.CHATS -> ChatDropdownItems(state, callback, dismiss)
|
MainNavigationListLocation.CHATS -> ChatDropdownItems(state, callback, dismiss)
|
||||||
MainNavigationListLocation.CALLS -> CallDropdownItems(state.callFilter, callback, dismiss)
|
MainNavigationListLocation.CALLS -> CallDropdownItems(state.callFilter, callback, dismiss)
|
||||||
MainNavigationListLocation.STORIES -> StoryDropDownItems(callback, dismiss)
|
MainNavigationListLocation.STORIES -> StoryDropDownItems(callback, dismiss)
|
||||||
|
|||||||
@@ -73,6 +73,33 @@ class MainToolbarViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun presentToolbarForConversationListFragment() {
|
||||||
|
setToolbarMode(MainToolbarMode.FULL, destination = MainNavigationListLocation.CHATS, overwriteSearchMode = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun presentToolbarForConversationListArchiveFragment() {
|
||||||
|
setToolbarMode(MainToolbarMode.BASIC, destination = MainNavigationListLocation.CHATS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun presentToolbarForStoriesLandingFragment() {
|
||||||
|
setToolbarMode(MainToolbarMode.FULL, destination = MainNavigationListLocation.STORIES)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun presentToolbarForCallLogFragment() {
|
||||||
|
setToolbarMode(MainToolbarMode.FULL, destination = MainNavigationListLocation.CALLS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun presentToolbarForMultiselect() {
|
||||||
|
setToolbarMode(MainToolbarMode.ACTION_MODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun presentToolbarForCurrentDestination() {
|
||||||
|
when (state.value.destination) {
|
||||||
|
MainNavigationListLocation.ARCHIVE -> setToolbarMode(MainToolbarMode.BASIC)
|
||||||
|
else -> setToolbarMode(MainToolbarMode.FULL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun setToolbarMode(
|
fun setToolbarMode(
|
||||||
mode: MainToolbarMode,
|
mode: MainToolbarMode,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.compose.ui.platform.ComposeView
|
|||||||
import androidx.core.app.ActivityOptionsCompat
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
||||||
@@ -66,7 +65,7 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
|||||||
|
|
||||||
private val lifecycleDisposable = LifecycleDisposable()
|
private val lifecycleDisposable = LifecycleDisposable()
|
||||||
|
|
||||||
private val viewModel: StoriesLandingViewModel by viewModels(
|
private val viewModel: StoriesLandingViewModel by activityViewModels(
|
||||||
factoryProducer = {
|
factoryProducer = {
|
||||||
StoriesLandingViewModel.Factory(StoriesLandingRepository(requireContext()))
|
StoriesLandingViewModel.Factory(StoriesLandingRepository(requireContext()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:viewBindingIgnore="true">
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/fragment_container"
|
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:navGraph="@navigation/main_activity_list" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/main_activity_list"
|
|
||||||
app:startDestination="@id/conversationListFragment">
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/conversationListFragment"
|
|
||||||
android:name="org.thoughtcrime.securesms.conversationlist.ConversationListFragment"
|
|
||||||
android:label="conversation_list_fragment">
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_conversationListFragment_to_conversationListArchiveFragment"
|
|
||||||
app:destination="@id/conversationListArchiveFragment"
|
|
||||||
app:enterAnim="@anim/slide_from_end"
|
|
||||||
app:exitAnim="@anim/slide_to_start"
|
|
||||||
app:popEnterAnim="@anim/slide_from_start"
|
|
||||||
app:popExitAnim="@anim/slide_to_end" />
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_conversationListFragment_to_storiesLandingFragment"
|
|
||||||
app:destination="@id/storiesLandingFragment" />
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_conversationListFragment_to_callLogFragment"
|
|
||||||
app:destination="@id/callLogFragment" />
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/conversationListArchiveFragment"
|
|
||||||
android:name="org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment"
|
|
||||||
android:label="conversation_list_archive_fragment" />
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/storiesLandingFragment"
|
|
||||||
android:name="org.thoughtcrime.securesms.stories.landing.StoriesLandingFragment"
|
|
||||||
android:label="stories_landing_fragment">
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_storiesLandingFragment_to_callLogFragment"
|
|
||||||
app:destination="@id/callLogFragment" />
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/callLogFragment"
|
|
||||||
android:name="org.thoughtcrime.securesms.calls.log.CallLogFragment"
|
|
||||||
android:label="call_log_fragment">
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_callLogFragment_to_storiesLandingFragment"
|
|
||||||
app:destination="@id/storiesLandingFragment" />
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_callLogFragment_to_createCallLinkBottomSheet"
|
|
||||||
app:destination="@id/createCallLinkBottomSheet" />
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<dialog
|
|
||||||
android:id="@+id/createCallLinkBottomSheet"
|
|
||||||
android:name="org.thoughtcrime.securesms.calls.links.create.CreateCallLinkBottomSheetDialogFragment"
|
|
||||||
android:label="create_call_link_bottom_sheet">
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_createCallLinkBottomSheet_to_editCallLinkNameDialogFragment"
|
|
||||||
app:destination="@id/editCallLinkNameDialogFragment"
|
|
||||||
app:enterAnim="@anim/fragment_open_enter"
|
|
||||||
app:exitAnim="@anim/fragment_close_exit"
|
|
||||||
app:popEnterAnim="@anim/fragment_close_enter"
|
|
||||||
app:popExitAnim="@anim/fragment_close_exit" />
|
|
||||||
</dialog>
|
|
||||||
|
|
||||||
<dialog
|
|
||||||
android:id="@+id/editCallLinkNameDialogFragment"
|
|
||||||
android:name="org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragment"
|
|
||||||
android:label="edit_call_link_name_fragment">
|
|
||||||
<argument
|
|
||||||
android:name="name"
|
|
||||||
app:argType="string"
|
|
||||||
app:nullable="false" />
|
|
||||||
|
|
||||||
</dialog>
|
|
||||||
|
|
||||||
</navigation>
|
|
||||||
Reference in New Issue
Block a user