From 6791d2d46eef7a9e0fde438d4af6802254cae8a5 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 2 May 2023 13:09:44 -0300 Subject: [PATCH] Add animation when switching from chats to calls. --- .../securesms/calls/log/CallLogFragment.kt | 26 +++++++++ .../main/MainActivityListHostFragment.kt | 20 ++++--- .../tabs/ConversationListTabsViewModel.kt | 2 +- app/src/main/res/layout/call_log_fragment.xml | 55 ++++++++++++++----- 4 files changed, 79 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt index ffed22b2c0..376c3db328 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt @@ -11,14 +11,18 @@ import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.app.SharedElementCallback import androidx.core.view.MenuProvider +import androidx.core.view.ViewCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import androidx.transition.TransitionInflater import com.google.android.material.appbar.AppBarLayout import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.kotlin.Flowables import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.DimensionUnit @@ -52,6 +56,7 @@ import org.thoughtcrime.securesms.util.doAfterNextLayout import org.thoughtcrime.securesms.util.fragments.requireListener import org.thoughtcrime.securesms.util.visible import java.util.Objects +import java.util.concurrent.TimeUnit /** * Call Log tab. @@ -94,6 +99,7 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal override fun onViewCreated(view: View, savedInstanceState: Bundle?) { requireActivity().addMenuProvider(menuProvider, viewLifecycleOwner) + initializeSharedElementTransition() val adapter = CallLogAdapter(this) disposables.bindTo(viewLifecycleOwner) @@ -181,6 +187,26 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal viewModel.markAllCallEventsRead() } + private fun initializeSharedElementTransition() { + ViewCompat.setTransitionName(binding.fab, "new_convo_fab") + ViewCompat.setTransitionName(binding.fabSharedElementTarget, "camera_fab") + + sharedElementEnterTransition = TransitionInflater.from(requireContext()).inflateTransition(R.transition.change_transform_fabs) + setEnterSharedElementCallback(object : SharedElementCallback() { + override fun onSharedElementStart(sharedElementNames: MutableList?, sharedElements: MutableList?, sharedElementSnapshots: MutableList?) { + if (sharedElementNames?.contains("camera_fab") == true) { + this@CallLogFragment.binding.fab.setImageResource(R.drawable.symbol_edit_24) + disposables += Single.timer(200, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy { + this@CallLogFragment.binding.fab.setImageResource(R.drawable.symbol_phone_plus_24) + this@CallLogFragment.binding.fabSharedElementTarget.alpha = 0f + } + } + } + }) + } + private fun initializeTapToScrollToTop(scrollToPositionDelegate: ScrollToPositionDelegate) { disposables += tabsViewModel.tabClickEvents .filter { it == ConversationListTab.CALLS } diff --git a/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt index a945257f6a..69476515df 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt @@ -112,7 +112,7 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f private fun goToStateFromConversationList(state: ConversationListTabsState, navController: NavController) { if (state.tab == ConversationListTab.CHATS) { return - } else if (state.tab == ConversationListTab.STORIES) { + } else { val cameraFab = requireView().findViewById(R.id.camera_fab) val newConvoFab = requireView().findViewById(R.id.fab) @@ -128,19 +128,18 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f ) } + val destination = if (state.tab == ConversationListTab.STORIES) { + R.id.action_conversationListFragment_to_storiesLandingFragment + } else { + R.id.action_conversationListFragment_to_callLogFragment + } + navController.navigate( - R.id.action_conversationListFragment_to_storiesLandingFragment, + destination, null, null, extras ) - } else { - navController.navigate( - R.id.action_conversationListFragment_to_callLogFragment, - null, - null, - null - ) } } @@ -350,14 +349,17 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f conversationListTabsViewModel.isShowingArchived(false) presentToolbarForConversationListFragment() } + R.id.conversationListArchiveFragment -> { conversationListTabsViewModel.isShowingArchived(true) presentToolbarForConversationListArchiveFragment() } + R.id.storiesLandingFragment -> { conversationListTabsViewModel.isShowingArchived(false) presentToolbarForStoriesLandingFragment() } + R.id.callLogFragment -> { conversationListTabsViewModel.isShowingArchived(false) presentToolbarForCallLogFragment() diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/tabs/ConversationListTabsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/tabs/ConversationListTabsViewModel.kt index e49e50c2ab..3ef421e50a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/tabs/ConversationListTabsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/tabs/ConversationListTabsViewModel.kt @@ -88,7 +88,7 @@ class ConversationListTabsViewModel(repository: ConversationListTabRepository) : } } - private fun performStoreUpdate(flowable: Flowable, fn: (T, ConversationListTabsState) -> ConversationListTabsState): Disposable { + private fun performStoreUpdate(flowable: Flowable, fn: (T, ConversationListTabsState) -> ConversationListTabsState): Disposable { return store.update(flowable) { t, state -> fn(t, state.copy(prevTab = state.tab)) } diff --git a/app/src/main/res/layout/call_log_fragment.xml b/app/src/main/res/layout/call_log_fragment.xml index 6fca1ff24c..c0f44fa46c 100644 --- a/app/src/main/res/layout/call_log_fragment.xml +++ b/app/src/main/res/layout/call_log_fragment.xml @@ -66,21 +66,48 @@ - + android:layout_gravity="bottom" + android:clipChildren="false" + android:clipToPadding="false" + app:layout_behavior="org.thoughtcrime.securesms.util.views.SlideUpWithSnackbarBehavior"> + + + + + +