Started splashscreen, waiting for first fragment to be displayed

This commit is contained in:
Sylvain Berfini 2023-11-13 16:16:29 +01:00
parent 6aa2f49321
commit 53ca7aef1a
13 changed files with 112 additions and 1 deletions

View file

@ -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"

View file

@ -50,6 +50,7 @@
<activity
android:name=".ui.main.MainActivity"
android:theme="@style/AppSplashScreenTheme"
android:windowSoftInputMode="adjustResize"
android:exported="true"
android:launchMode="singleTask">

View file

@ -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?) {

View file

@ -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
}
}

View file

@ -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) {

View file

@ -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
) {

View file

@ -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(

View file

@ -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
) {

View file

@ -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) {

View file

@ -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
) {

View file

@ -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()
}
}

View file

@ -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<Boolean>()
@ -73,6 +76,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
/* Contacts related */
val contactsReadyEvent = MutableLiveData<Event<Boolean>>()
val showContactEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
@ -85,6 +90,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
/* Call logs related */
val historyReadyEvent = MutableLiveData<Event<Boolean>>()
val showStartCallEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
@ -103,6 +110,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
/* Conversation related */
val conversationsReadyEvent = MutableLiveData<Event<Boolean>>()
val showStartConversationEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
@ -119,6 +128,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
/* Meetings related */
val meetingsReadyEvent = MutableLiveData<Event<Boolean>>()
val showScheduleMeetingEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}

View file

@ -1,5 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppSplashScreenTheme" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/black</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/linphone</item>
<item name="postSplashScreenTheme">@style/Theme.Linphone</item>
</style>
<style name="Theme.Linphone" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/orange_main_500</item>