mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-04-28 22:16:22 +00:00
Improved startup reactivity
This commit is contained in:
parent
f6545f5641
commit
26e30c6060
4 changed files with 422 additions and 406 deletions
|
|
@ -25,6 +25,7 @@ import android.os.Bundle
|
||||||
import android.provider.ContactsContract
|
import android.provider.ContactsContract
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.loader.app.LoaderManager
|
import androidx.loader.app.LoaderManager
|
||||||
import androidx.loader.content.CursorLoader
|
import androidx.loader.content.CursorLoader
|
||||||
import androidx.loader.content.Loader
|
import androidx.loader.content.Loader
|
||||||
|
|
@ -56,6 +57,8 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
private const val MIN_INTERVAL_TO_WAIT_BEFORE_REFRESH = 300000L // 5 minutes
|
private const val MIN_INTERVAL_TO_WAIT_BEFORE_REFRESH = 300000L // 5 minutes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val friends = HashMap<String, Friend>()
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
|
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
|
||||||
val mimeType = ContactsContract.Data.MIMETYPE
|
val mimeType = ContactsContract.Data.MIMETYPE
|
||||||
|
|
@ -92,29 +95,71 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
}
|
}
|
||||||
Log.i("$TAG Load finished, found ${cursor.count} entries in cursor")
|
Log.i("$TAG Load finished, found ${cursor.count} entries in cursor")
|
||||||
|
|
||||||
coreContext.postOnCoreThread { core ->
|
coreContext.postOnCoreThread {
|
||||||
val state = coreContext.core.globalState
|
parseFriends(cursor)
|
||||||
if (state == GlobalState.Shutdown || state == GlobalState.Off) {
|
}
|
||||||
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
}
|
||||||
return@postOnCoreThread
|
|
||||||
|
@MainThread
|
||||||
|
override fun onLoaderReset(loader: Loader<Cursor>) {
|
||||||
|
Log.i("$TAG Loader reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private fun parseFriends(cursor: Cursor) {
|
||||||
|
val core = coreContext.core
|
||||||
|
|
||||||
|
val state = core.globalState
|
||||||
|
if (state == GlobalState.Shutdown || state == GlobalState.Off) {
|
||||||
|
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val friends = HashMap<String, Friend>()
|
|
||||||
try {
|
try {
|
||||||
// Cursor can be null now that we are on a different dispatcher according to Crashlytics
|
|
||||||
val friendsPhoneNumbers = arrayListOf<String>()
|
val friendsPhoneNumbers = arrayListOf<String>()
|
||||||
val friendsAddresses = arrayListOf<Address>()
|
val friendsAddresses = arrayListOf<Address>()
|
||||||
var previousId = ""
|
var previousId = ""
|
||||||
while (cursor != null && !cursor.isClosed && cursor.moveToNext()) {
|
|
||||||
|
val contactIdColumn = cursor.getColumnIndexOrThrow(ContactsContract.Data.CONTACT_ID)
|
||||||
|
val mimetypeColumn = cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)
|
||||||
|
val displayNameColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.Data.DISPLAY_NAME_PRIMARY
|
||||||
|
)
|
||||||
|
val starredColumn = cursor.getColumnIndexOrThrow(ContactsContract.Contacts.STARRED)
|
||||||
|
val lookupColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.Contacts.LOOKUP_KEY
|
||||||
|
)
|
||||||
|
val phoneNumberColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.NUMBER
|
||||||
|
)
|
||||||
|
val phoneTypeColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.TYPE
|
||||||
|
)
|
||||||
|
val phoneLabelColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.LABEL
|
||||||
|
)
|
||||||
|
val normalizedPhoneColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
|
||||||
|
)
|
||||||
|
val sipAddressColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS
|
||||||
|
)
|
||||||
|
val companyColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Organization.COMPANY
|
||||||
|
)
|
||||||
|
val jobTitleColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.Organization.TITLE
|
||||||
|
)
|
||||||
|
val givenNameColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
|
||||||
|
)
|
||||||
|
val familyNameColumn = cursor.getColumnIndexOrThrow(
|
||||||
|
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
|
||||||
|
)
|
||||||
|
while (!cursor.isClosed && cursor.moveToNext()) {
|
||||||
try {
|
try {
|
||||||
val id: String =
|
val id: String = cursor.getString(contactIdColumn)
|
||||||
cursor.getString(
|
val mime: String? = cursor.getString(mimetypeColumn)
|
||||||
cursor.getColumnIndexOrThrow(ContactsContract.Data.CONTACT_ID)
|
|
||||||
)
|
|
||||||
val mime: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (previousId.isEmpty() || previousId != id) {
|
if (previousId.isEmpty() || previousId != id) {
|
||||||
friendsPhoneNumbers.clear()
|
friendsPhoneNumbers.clear()
|
||||||
|
|
@ -125,12 +170,7 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
val friend = friends[id] ?: core.createFriend()
|
val friend = friends[id] ?: core.createFriend()
|
||||||
friend.refKey = id
|
friend.refKey = id
|
||||||
if (friend.name.isNullOrEmpty()) {
|
if (friend.name.isNullOrEmpty()) {
|
||||||
val displayName: String? =
|
val displayName: String? = cursor.getString(displayNameColumn)
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.Data.DISPLAY_NAME_PRIMARY
|
|
||||||
)
|
|
||||||
)
|
|
||||||
friend.name = displayName
|
friend.name = displayName
|
||||||
|
|
||||||
val uri = friend.getNativeContactPictureUri()
|
val uri = friend.getNativeContactPictureUri()
|
||||||
|
|
@ -138,18 +178,11 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
friend.photo = uri.toString()
|
friend.photo = uri.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val starred =
|
val starred = cursor.getInt(starredColumn) == 1
|
||||||
cursor.getInt(
|
|
||||||
cursor.getColumnIndexOrThrow(ContactsContract.Contacts.STARRED)
|
|
||||||
) == 1
|
|
||||||
friend.starred = starred
|
friend.starred = starred
|
||||||
|
|
||||||
val lookupKey =
|
val lookupKey =
|
||||||
cursor.getString(
|
cursor.getString(lookupColumn)
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.Contacts.LOOKUP_KEY
|
|
||||||
)
|
|
||||||
)
|
|
||||||
friend.nativeUri =
|
friend.nativeUri =
|
||||||
"${ContactsContract.Contacts.CONTENT_LOOKUP_URI}/$lookupKey"
|
"${ContactsContract.Contacts.CONTENT_LOOKUP_URI}/$lookupKey"
|
||||||
|
|
||||||
|
|
@ -160,30 +193,10 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
when (mime) {
|
when (mime) {
|
||||||
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
|
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
|
||||||
val data1: String? =
|
val data1: String? = cursor.getString(phoneNumberColumn)
|
||||||
cursor.getString(
|
val data2: String? = cursor.getString(phoneTypeColumn)
|
||||||
cursor.getColumnIndexOrThrow(
|
val data3: String? = cursor.getString(phoneLabelColumn)
|
||||||
ContactsContract.CommonDataKinds.Phone.NUMBER
|
val data4: String? = cursor.getString(normalizedPhoneColumn)
|
||||||
)
|
|
||||||
)
|
|
||||||
val data2: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Phone.TYPE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val data3: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Phone.LABEL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val data4: String? =
|
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val label =
|
val label =
|
||||||
PhoneNumberUtils.addressBookLabelTypeToVcardParamString(
|
PhoneNumberUtils.addressBookLabelTypeToVcardParamString(
|
||||||
|
|
@ -218,12 +231,7 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> {
|
ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> {
|
||||||
val sipAddress: String? =
|
val sipAddress: String? = cursor.getString(sipAddressColumn)
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (sipAddress != null) {
|
if (sipAddress != null) {
|
||||||
val address = core.interpretUrl(sipAddress, false)
|
val address = core.interpretUrl(sipAddress, false)
|
||||||
if (address != null &&
|
if (address != null &&
|
||||||
|
|
@ -237,22 +245,12 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> {
|
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE -> {
|
||||||
val organization: String? =
|
val organization: String? = cursor.getString(companyColumn)
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Organization.COMPANY
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (organization != null) {
|
if (organization != null) {
|
||||||
friend.organization = organization
|
friend.organization = organization
|
||||||
}
|
}
|
||||||
|
|
||||||
val job: String? =
|
val job: String? = cursor.getString(jobTitleColumn)
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.Organization.TITLE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (job != null) {
|
if (job != null) {
|
||||||
friend.jobTitle = job
|
friend.jobTitle = job
|
||||||
}
|
}
|
||||||
|
|
@ -260,22 +258,12 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
|
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
|
||||||
val vCard = friend.vcard
|
val vCard = friend.vcard
|
||||||
if (vCard != null) {
|
if (vCard != null) {
|
||||||
val givenName: String? =
|
val givenName: String? = cursor.getString(givenNameColumn)
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (!givenName.isNullOrEmpty()) {
|
if (!givenName.isNullOrEmpty()) {
|
||||||
vCard.givenName = givenName
|
vCard.givenName = givenName
|
||||||
}
|
}
|
||||||
|
|
||||||
val familyName: String? =
|
val familyName: String? = cursor.getString(familyNameColumn)
|
||||||
cursor.getString(
|
|
||||||
cursor.getColumnIndexOrThrow(
|
|
||||||
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (!familyName.isNullOrEmpty()) {
|
if (!familyName.isNullOrEmpty()) {
|
||||||
vCard.familyName = familyName
|
vCard.familyName = familyName
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +277,24 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.i("$TAG Contacts parsed, posting another task to handle adding them (or not)")
|
||||||
|
// Re-post another task to allow other tasks on Core thread
|
||||||
|
coreContext.postOnCoreThread {
|
||||||
|
addFriendsIfNeeded()
|
||||||
|
}
|
||||||
|
} catch (sde: StaleDataException) {
|
||||||
|
Log.e("$TAG State Data Exception: $sde")
|
||||||
|
} catch (ise: IllegalStateException) {
|
||||||
|
Log.e("$TAG Illegal State Exception: $ise")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("$TAG Exception: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private fun addFriendsIfNeeded() {
|
||||||
|
val core = coreContext.core
|
||||||
|
|
||||||
if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) {
|
if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) {
|
||||||
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
Log.w("$TAG Core is being stopped or already destroyed, abort")
|
||||||
} else if (friends.isEmpty()) {
|
} else if (friends.isEmpty()) {
|
||||||
|
|
@ -296,12 +302,14 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
} else {
|
} else {
|
||||||
Log.i("$TAG ${friends.size} friends fetched")
|
Log.i("$TAG ${friends.size} friends fetched")
|
||||||
|
|
||||||
val friendsList = core.getFriendListByName(NATIVE_ADDRESS_BOOK_FRIEND_LIST) ?: core.createFriendList()
|
val friendsList = core.getFriendListByName(NATIVE_ADDRESS_BOOK_FRIEND_LIST)
|
||||||
|
?: core.createFriendList()
|
||||||
if (friendsList.displayName.isNullOrEmpty()) {
|
if (friendsList.displayName.isNullOrEmpty()) {
|
||||||
Log.i(
|
Log.i(
|
||||||
"$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] didn't exist yet, let's create it"
|
"$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] didn't exist yet, let's create it"
|
||||||
)
|
)
|
||||||
friendsList.isDatabaseStorageEnabled = true // Store them to keep presence info available for push notifications & favorites
|
friendsList.isDatabaseStorageEnabled =
|
||||||
|
true // Store them to keep presence info available for push notifications & favorites
|
||||||
friendsList.type = FriendList.Type.Default
|
friendsList.type = FriendList.Type.Default
|
||||||
friendsList.displayName = NATIVE_ADDRESS_BOOK_FRIEND_LIST
|
friendsList.displayName = NATIVE_ADDRESS_BOOK_FRIEND_LIST
|
||||||
core.addFriendList(friendsList)
|
core.addFriendList(friendsList)
|
||||||
|
|
@ -321,7 +329,8 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
"$TAG Friend [${localFriend.name}] with ref key [${localFriend.refKey}] found in newly fetched batch"
|
"$TAG Friend [${localFriend.name}] with ref key [${localFriend.refKey}] found in newly fetched batch"
|
||||||
)
|
)
|
||||||
localFriend.edit()
|
localFriend.edit()
|
||||||
localFriend.nativeUri = newlyFetchedFriend.nativeUri // Native URI isn't stored in linphone database, needs to be updated
|
localFriend.nativeUri =
|
||||||
|
newlyFetchedFriend.nativeUri // Native URI isn't stored in linphone database, needs to be updated
|
||||||
|
|
||||||
// Update basic fields that may have changed
|
// Update basic fields that may have changed
|
||||||
localFriend.name = newlyFetchedFriend.name
|
localFriend.name = newlyFetchedFriend.name
|
||||||
|
|
@ -401,18 +410,5 @@ class ContactLoader : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
Log.i("$TAG Subscription(s) updated")
|
Log.i("$TAG Subscription(s) updated")
|
||||||
coreContext.contactsManager.onNativeContactsLoaded()
|
coreContext.contactsManager.onNativeContactsLoaded()
|
||||||
}
|
}
|
||||||
} catch (sde: StaleDataException) {
|
|
||||||
Log.e("$TAG State Data Exception: $sde")
|
|
||||||
} catch (ise: IllegalStateException) {
|
|
||||||
Log.e("$TAG Illegal State Exception: $ise")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("$TAG Exception: $e")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainThread
|
|
||||||
override fun onLoaderReset(loader: Loader<Cursor>) {
|
|
||||||
Log.i("$TAG Loader reset")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ class ContactsManager @UiThread constructor() {
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
fun loadContacts(activity: MainActivity) {
|
fun loadContacts(activity: MainActivity) {
|
||||||
|
Log.i("$TAG Starting contacts loader")
|
||||||
val manager = LoaderManager.getInstance(activity)
|
val manager = LoaderManager.getInstance(activity)
|
||||||
manager.restartLoader(0, null, ContactLoader())
|
manager.restartLoader(0, null, ContactLoader())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import androidx.annotation.MainThread
|
import android.view.ViewTreeObserver
|
||||||
import androidx.annotation.UiThread
|
import androidx.annotation.UiThread
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.doOnAttach
|
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavDestination
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
|
|
@ -82,6 +82,20 @@ class MainActivity : GenericActivity() {
|
||||||
|
|
||||||
private var currentlyDisplayedAuthDialog: Dialog? = null
|
private var currentlyDisplayedAuthDialog: Dialog? = null
|
||||||
|
|
||||||
|
private var navigatedToDefaultFragment = false
|
||||||
|
|
||||||
|
private val destinationListener = object : NavController.OnDestinationChangedListener {
|
||||||
|
override fun onDestinationChanged(
|
||||||
|
controller: NavController,
|
||||||
|
destination: NavDestination,
|
||||||
|
arguments: Bundle?
|
||||||
|
) {
|
||||||
|
Log.i("$TAG Latest visited fragment was restored")
|
||||||
|
navigatedToDefaultFragment = true
|
||||||
|
controller.removeOnDestinationChangedListener(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
// Must be done before the setContentView
|
// Must be done before the setContentView
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
|
|
@ -165,14 +179,23 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.root.doOnAttach {
|
// Wait for latest visited fragment to be displayed before hiding the splashscreen
|
||||||
|
binding.root.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
|
||||||
|
override fun onPreDraw(): Boolean {
|
||||||
|
return if (navigatedToDefaultFragment) {
|
||||||
Log.i("$TAG Report UI has been fully drawn (TTFD)")
|
Log.i("$TAG Report UI has been fully drawn (TTFD)")
|
||||||
try {
|
try {
|
||||||
reportFullyDrawn()
|
reportFullyDrawn()
|
||||||
} catch (se: SecurityException) {
|
} catch (se: SecurityException) {
|
||||||
Log.e("$TAG Security exception when doing reportFullyDrawn(): $se")
|
Log.e("$TAG Security exception when doing reportFullyDrawn(): $se")
|
||||||
}
|
}
|
||||||
|
binding.root.viewTreeObserver.removeOnPreDrawListener(this)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
coreContext.bearerAuthenticationRequestedEvent.observe(this) {
|
coreContext.bearerAuthenticationRequestedEvent.observe(this) {
|
||||||
it.consume { pair ->
|
it.consume { pair ->
|
||||||
|
|
@ -224,8 +247,10 @@ class MainActivity : GenericActivity() {
|
||||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||||
super.onPostCreate(savedInstanceState)
|
super.onPostCreate(savedInstanceState)
|
||||||
|
|
||||||
|
goToLatestVisitedFragment()
|
||||||
|
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
handleIntent(intent, false)
|
handleIntent(intent)
|
||||||
} else {
|
} else {
|
||||||
// This should never happen!
|
// This should never happen!
|
||||||
Log.e("$TAG onPostCreate called without intent !")
|
Log.e("$TAG onPostCreate called without intent !")
|
||||||
|
|
@ -270,7 +295,7 @@ class MainActivity : GenericActivity() {
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
handleIntent(intent, true)
|
handleIntent(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RtlHardcoded")
|
@SuppressLint("RtlHardcoded")
|
||||||
|
|
@ -294,8 +319,71 @@ class MainActivity : GenericActivity() {
|
||||||
return findNavController(R.id.main_nav_host_fragment)
|
return findNavController(R.id.main_nav_host_fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
private fun goToLatestVisitedFragment() {
|
||||||
private fun handleIntent(intent: Intent, isNewIntent: Boolean) {
|
try {
|
||||||
|
// Prevent navigating to default fragment upon rotation (we only want to do it on first start)
|
||||||
|
if (intent.action == Intent.ACTION_MAIN && intent.type == null && intent.data == null) {
|
||||||
|
if (viewModel.mainIntentHandled) {
|
||||||
|
Log.d(
|
||||||
|
"$TAG Main intent without type nor data was already handled, do nothing"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
viewModel.mainIntentHandled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultFragmentId = getPreferences(Context.MODE_PRIVATE).getInt(
|
||||||
|
DEFAULT_FRAGMENT_KEY,
|
||||||
|
HISTORY_FRAGMENT_ID
|
||||||
|
)
|
||||||
|
Log.i(
|
||||||
|
"$TAG Trying to navigate to set default destination [$defaultFragmentId]"
|
||||||
|
)
|
||||||
|
val args = intent.extras
|
||||||
|
try {
|
||||||
|
val navOptionsBuilder = NavOptions.Builder()
|
||||||
|
navOptionsBuilder.setPopUpTo(R.id.historyListFragment, true)
|
||||||
|
navOptionsBuilder.setLaunchSingleTop(true)
|
||||||
|
val navOptions = navOptionsBuilder.build()
|
||||||
|
when (defaultFragmentId) {
|
||||||
|
CONTACTS_FRAGMENT_ID -> {
|
||||||
|
findNavController().addOnDestinationChangedListener(destinationListener)
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.contactsListFragment,
|
||||||
|
args,
|
||||||
|
navOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CHAT_FRAGMENT_ID -> {
|
||||||
|
findNavController().addOnDestinationChangedListener(destinationListener)
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.conversationsListFragment,
|
||||||
|
args,
|
||||||
|
navOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MEETINGS_FRAGMENT_ID -> {
|
||||||
|
findNavController().addOnDestinationChangedListener(destinationListener)
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.meetingsListFragment,
|
||||||
|
args,
|
||||||
|
navOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Log.i("$TAG Default fragment is the same as the latest visited one")
|
||||||
|
navigatedToDefaultFragment = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ise: IllegalStateException) {
|
||||||
|
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
||||||
|
}
|
||||||
|
} catch (ise: IllegalStateException) {
|
||||||
|
Log.i("$TAG Failed to handle intent: $ise")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIntent(intent: Intent) {
|
||||||
Log.i(
|
Log.i(
|
||||||
"$TAG Handling intent action [${intent.action}], type [${intent.type}] and data [${intent.data}]"
|
"$TAG Handling intent action [${intent.action}], type [${intent.type}] and data [${intent.data}]"
|
||||||
)
|
)
|
||||||
|
|
@ -326,12 +414,11 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
handleMainIntent(intent, isNewIntent)
|
handleMainIntent(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleLocusOrShortcut(id: String) {
|
private fun handleLocusOrShortcut(id: String) {
|
||||||
Log.i("$TAG Found locus ID [$id]")
|
Log.i("$TAG Found locus ID [$id]")
|
||||||
val pair = LinphoneUtils.getLocalAndPeerSipUrisFromChatRoomId(id)
|
val pair = LinphoneUtils.getLocalAndPeerSipUrisFromChatRoomId(id)
|
||||||
|
|
@ -345,8 +432,7 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
private fun handleMainIntent(intent: Intent) {
|
||||||
private fun handleMainIntent(intent: Intent, isNewIntent: Boolean) {
|
|
||||||
coreContext.postOnCoreThread { core ->
|
coreContext.postOnCoreThread { core ->
|
||||||
if (corePreferences.firstLaunch) {
|
if (corePreferences.firstLaunch) {
|
||||||
Log.i("$TAG First time Linphone 6.0 has been started, showing Welcome activity")
|
Log.i("$TAG First time Linphone 6.0 has been started, showing Welcome activity")
|
||||||
|
|
@ -361,18 +447,6 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
coreContext.postOnMainThread {
|
coreContext.postOnMainThread {
|
||||||
// Prevent navigating to default fragment upon rotation (we only want to do it on first start)
|
|
||||||
if (intent.action == Intent.ACTION_MAIN && intent.type == null && intent.data == null && !isNewIntent) {
|
|
||||||
if (viewModel.mainIntentHandled) {
|
|
||||||
Log.d(
|
|
||||||
"$TAG Main intent without type nor data was already handled, do nothing"
|
|
||||||
)
|
|
||||||
return@postOnMainThread
|
|
||||||
} else {
|
|
||||||
viewModel.mainIntentHandled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intent.hasExtra("Chat")) {
|
if (intent.hasExtra("Chat")) {
|
||||||
Log.i("$TAG Intent has [Chat] extra")
|
Log.i("$TAG Intent has [Chat] extra")
|
||||||
try {
|
try {
|
||||||
|
|
@ -404,64 +478,12 @@ class MainActivity : GenericActivity() {
|
||||||
} catch (ise: IllegalStateException) {
|
} catch (ise: IllegalStateException) {
|
||||||
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
||||||
}
|
}
|
||||||
} else if (!isNewIntent) {
|
|
||||||
try {
|
|
||||||
val defaultFragmentId = getPreferences(Context.MODE_PRIVATE).getInt(
|
|
||||||
DEFAULT_FRAGMENT_KEY,
|
|
||||||
HISTORY_FRAGMENT_ID
|
|
||||||
)
|
|
||||||
Log.i(
|
|
||||||
"$TAG Trying to navigate to set default destination [$defaultFragmentId]"
|
|
||||||
)
|
|
||||||
val args = intent.extras
|
|
||||||
try {
|
|
||||||
val navOptionsBuilder = NavOptions.Builder()
|
|
||||||
navOptionsBuilder.setPopUpTo(R.id.historyListFragment, true)
|
|
||||||
navOptionsBuilder.setLaunchSingleTop(true)
|
|
||||||
val navOptions = navOptionsBuilder.build()
|
|
||||||
when (defaultFragmentId) {
|
|
||||||
HISTORY_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.historyListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
CONTACTS_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.contactsListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
CHAT_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.conversationsListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MEETINGS_FRAGMENT_ID -> {
|
|
||||||
findNavController().navigate(
|
|
||||||
R.id.meetingsListFragment,
|
|
||||||
args,
|
|
||||||
navOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ise: IllegalStateException) {
|
|
||||||
Log.e("$TAG Can't navigate to Conversations fragment: $ise")
|
|
||||||
}
|
|
||||||
} catch (ise: IllegalStateException) {
|
|
||||||
Log.i("$TAG Failed to handle intent: $ise")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleSendIntent(intent: Intent, multiple: Boolean) {
|
private fun handleSendIntent(intent: Intent, multiple: Boolean) {
|
||||||
val parcelablesUri = arrayListOf<Uri>()
|
val parcelablesUri = arrayListOf<Uri>()
|
||||||
|
|
||||||
|
|
@ -550,7 +572,6 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun parseShortcutIfAny(intent: Intent): Pair<String, String>? {
|
private fun parseShortcutIfAny(intent: Intent): Pair<String, String>? {
|
||||||
val shortcutId = intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
|
val shortcutId = intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
|
||||||
if (shortcutId != null) {
|
if (shortcutId != null) {
|
||||||
|
|
@ -562,7 +583,6 @@ class MainActivity : GenericActivity() {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleCallIntent(intent: Intent) {
|
private fun handleCallIntent(intent: Intent) {
|
||||||
val uri = intent.data?.toString()
|
val uri = intent.data?.toString()
|
||||||
if (uri.isNullOrEmpty()) {
|
if (uri.isNullOrEmpty()) {
|
||||||
|
|
@ -595,7 +615,6 @@ class MainActivity : GenericActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
private fun handleConfigIntent(uri: String) {
|
private fun handleConfigIntent(uri: String) {
|
||||||
val remoteConfigUri = uri.substring("linphone-config:".length)
|
val remoteConfigUri = uri.substring("linphone-config:".length)
|
||||||
val url = when {
|
val url = when {
|
||||||
|
|
|
||||||
|
|
@ -699,6 +699,12 @@ class MessageModel @WorkerThread constructor(
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun startVoiceRecordPlayer() {
|
private fun startVoiceRecordPlayer() {
|
||||||
|
if (voiceRecordAudioFocusRequest == null) {
|
||||||
|
voiceRecordAudioFocusRequest = AudioUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
||||||
|
coreContext.context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (isPlayerClosed()) {
|
if (isPlayerClosed()) {
|
||||||
Log.w("$TAG Player closed, let's open it first")
|
Log.w("$TAG Player closed, let's open it first")
|
||||||
initVoiceRecordPlayer()
|
initVoiceRecordPlayer()
|
||||||
|
|
@ -712,12 +718,6 @@ class MessageModel @WorkerThread constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (voiceRecordAudioFocusRequest == null) {
|
|
||||||
voiceRecordAudioFocusRequest = AudioUtils.acquireAudioFocusForVoiceRecordingOrPlayback(
|
|
||||||
coreContext.context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i("$TAG Playing voice record")
|
Log.i("$TAG Playing voice record")
|
||||||
isPlayingVoiceRecord.postValue(true)
|
isPlayingVoiceRecord.postValue(true)
|
||||||
voiceRecordPlayer.start()
|
voiceRecordPlayer.start()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue