diff --git a/app/build.gradle b/app/build.gradle index c8a8e0642..0b8735505 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,6 +75,7 @@ dependencies { implementation "androidx.appcompat:appcompat:1.7.0-alpha03" implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.core:core-ktx:1.12.0" + implementation 'androidx.core:core-splashscreen:1.0.1' implementation "androidx.core:core-telecom:1.0.0-alpha02" implementation "androidx.media:media:1.6.0" implementation "androidx.fragment:fragment-ktx:1.6.2" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cc1acb824..085555775 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -50,6 +50,7 @@ diff --git a/app/src/main/java/org/linphone/ui/main/MainActivity.kt b/app/src/main/java/org/linphone/ui/main/MainActivity.kt index b115113bd..41ad4bbc6 100644 --- a/app/src/main/java/org/linphone/ui/main/MainActivity.kt +++ b/app/src/main/java/org/linphone/ui/main/MainActivity.kt @@ -28,11 +28,14 @@ import android.os.Bundle import android.os.Parcelable import android.view.Gravity import android.view.ViewGroup +import android.view.ViewTreeObserver import androidx.annotation.DrawableRes import androidx.annotation.UiThread import androidx.appcompat.app.AppCompatActivity +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat import androidx.core.view.children +import androidx.core.view.doOnAttach import androidx.databinding.DataBindingUtil import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -76,6 +79,9 @@ class MainActivity : AppCompatActivity() { private lateinit var sharedViewModel: SharedMainViewModel override fun onCreate(savedInstanceState: Bundle?) { + // Must be done before the setContentView + installSplashScreen() + WindowCompat.setDecorFitsSystemWindows(window, true) super.onCreate(savedInstanceState) @@ -145,6 +151,29 @@ class MainActivity : AppCompatActivity() { } } } + + binding.root.doOnAttach { + Log.i("$TAG Report UI has been fully drawn (TTFD)") + try { + reportFullyDrawn() + } catch (se: SecurityException) { + Log.e("$TAG Security exception when doing reportFullyDrawn(): $se") + } + } + + // Wait for fragment to be displayed before hiding the splashscreen + binding.root.viewTreeObserver.addOnPreDrawListener( + object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + return if (sharedViewModel.isFirstFragmentReady) { + binding.root.viewTreeObserver.removeOnPreDrawListener(this) + true + } else { + false + } + } + } + ) } override fun onPostCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt index eca71afce..ac39dad3e 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsFragment.kt @@ -66,6 +66,7 @@ class ConversationsFragment : GenericFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + postponeEnterTransition() super.onViewCreated(view, savedInstanceState) binding.lifecycleOwner = viewLifecycleOwner @@ -82,6 +83,15 @@ class ConversationsFragment : GenericFragment() { ) } + sharedViewModel.conversationsReadyEvent.observe(viewLifecycleOwner) { + it.consume { + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.isFirstFragmentReady = true + } + } + } + val args = arguments if (args != null) { val localSipUri = args.getString("LocalSipUri") @@ -194,6 +204,7 @@ class ConversationsFragment : GenericFragment() { override fun onResume() { super.onResume() + sharedViewModel.currentlyDisplayedFragment.value = R.id.conversationsFragment } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt index ee4f12ffd..e9c283110 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt @@ -25,6 +25,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.UiThread +import androidx.core.view.doOnPreDraw import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import org.linphone.R @@ -59,6 +60,7 @@ class ConversationsListFragment : AbstractTopBarFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + postponeEnterTransition() super.onViewCreated(view, savedInstanceState) listViewModel = ViewModelProvider(this)[ConversationsListViewModel::class.java] @@ -127,6 +129,11 @@ class ConversationsListFragment : AbstractTopBarFragment() { if (currentCount < it.size) { binding.conversationsList.scrollToPosition(0) } + + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.conversationsReadyEvent.value = Event(true) + } } listViewModel.chatRoomsReOrderedEvent.observe(viewLifecycleOwner) { diff --git a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsFragment.kt b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsFragment.kt index cb4480b4a..19e1afe0e 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsFragment.kt @@ -62,6 +62,7 @@ class ContactsFragment : GenericFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + postponeEnterTransition() super.onViewCreated(view, savedInstanceState) binding.lifecycleOwner = viewLifecycleOwner @@ -78,6 +79,15 @@ class ContactsFragment : GenericFragment() { ) } + sharedViewModel.contactsReadyEvent.observe(viewLifecycleOwner) { + it.consume { + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.isFirstFragmentReady = true + } + } + } + sharedViewModel.closeSlidingPaneEvent.observe( viewLifecycleOwner ) { diff --git a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt index 0dab96121..8bff1a1fa 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListFragment.kt @@ -30,6 +30,7 @@ import android.view.ViewGroup import android.widget.PopupWindow import androidx.annotation.UiThread import androidx.core.content.FileProvider +import androidx.core.view.doOnPreDraw import androidx.databinding.DataBindingUtil import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager @@ -68,6 +69,7 @@ class ContactsListFragment : AbstractTopBarFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + postponeEnterTransition() super.onViewCreated(view, savedInstanceState) listViewModel = ViewModelProvider(this)[ContactsListViewModel::class.java] @@ -101,6 +103,11 @@ class ContactsListFragment : AbstractTopBarFragment() { Log.i("$TAG Contacts list updated with new items, scrolling to top") binding.contactsList.smoothScrollToPosition(0) } + + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.contactsReadyEvent.value = Event(true) + } } listViewModel.favourites.observe( diff --git a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt index 03bb38296..26374a30e 100644 --- a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryFragment.kt @@ -62,6 +62,7 @@ class HistoryFragment : GenericFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + postponeEnterTransition() super.onViewCreated(view, savedInstanceState) binding.lifecycleOwner = viewLifecycleOwner @@ -78,6 +79,15 @@ class HistoryFragment : GenericFragment() { ) } + sharedViewModel.historyReadyEvent.observe(viewLifecycleOwner) { + it.consume { + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.isFirstFragmentReady = true + } + } + } + sharedViewModel.closeSlidingPaneEvent.observe( viewLifecycleOwner ) { diff --git a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt index 97ed38f23..1a3ca6448 100644 --- a/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/history/fragment/HistoryListFragment.kt @@ -30,6 +30,7 @@ import android.view.View import android.view.ViewGroup import android.widget.PopupWindow import androidx.annotation.UiThread +import androidx.core.view.doOnPreDraw import androidx.databinding.DataBindingUtil import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager @@ -69,6 +70,7 @@ class HistoryListFragment : AbstractTopBarFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + postponeEnterTransition() super.onViewCreated(view, savedInstanceState) listViewModel = ViewModelProvider(this)[HistoryListViewModel::class.java] @@ -150,6 +152,11 @@ class HistoryListFragment : AbstractTopBarFragment() { if (currentCount < it.size) { binding.historyList.scrollToPosition(0) } + + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.historyReadyEvent.value = Event(true) + } } listViewModel.historyDeletedEvent.observe(viewLifecycleOwner) { diff --git a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsFragment.kt b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsFragment.kt index c31a257c2..c6133b063 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsFragment.kt @@ -65,6 +65,7 @@ class MeetingsFragment : GenericFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + postponeEnterTransition() super.onViewCreated(view, savedInstanceState) binding.lifecycleOwner = viewLifecycleOwner @@ -81,6 +82,15 @@ class MeetingsFragment : GenericFragment() { ) } + sharedViewModel.meetingsReadyEvent.observe(viewLifecycleOwner) { + it.consume { + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.isFirstFragmentReady = true + } + } + } + sharedViewModel.closeSlidingPaneEvent.observe( viewLifecycleOwner ) { diff --git a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt index 29d1468f9..8b0f3497d 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt @@ -61,8 +61,8 @@ class MeetingsListFragment : AbstractTopBarFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) postponeEnterTransition() + super.onViewCreated(view, savedInstanceState) listViewModel = ViewModelProvider(this)[MeetingsListViewModel::class.java] @@ -100,6 +100,7 @@ class MeetingsListFragment : AbstractTopBarFragment() { if (currentCount < it.size) { (view.parent as? ViewGroup)?.doOnPreDraw { startPostponedEnterTransition() + sharedViewModel.meetingsReadyEvent.value = Event(true) scrollToToday() } } diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt index 4be2ff6b9..f1127ffdd 100644 --- a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt @@ -27,6 +27,9 @@ import org.linphone.core.ChatRoom import org.linphone.utils.Event class SharedMainViewModel @UiThread constructor() : ViewModel() { + // When set to true, it will hide the splashscreen + var isFirstFragmentReady: Boolean = false + /* Sliding Pane & navigation related */ val isSlidingPaneSlideable = MutableLiveData() @@ -73,6 +76,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() { /* Contacts related */ + val contactsReadyEvent = MutableLiveData>() + val showContactEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -85,6 +90,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() { /* Call logs related */ + val historyReadyEvent = MutableLiveData>() + val showStartCallEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -103,6 +110,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() { /* Conversation related */ + val conversationsReadyEvent = MutableLiveData>() + val showStartConversationEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -119,6 +128,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() { /* Meetings related */ + val meetingsReadyEvent = MutableLiveData>() + val showScheduleMeetingEvent: MutableLiveData> by lazy { MutableLiveData>() } diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 02344ec9d..e9727aad9 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,11 @@ + +