diff --git a/app/src/main/assets/linphonerc_factory b/app/src/main/assets/linphonerc_factory index 045de9d44..6961f344f 100644 --- a/app/src/main/assets/linphonerc_factory +++ b/app/src/main/assets/linphonerc_factory @@ -38,6 +38,7 @@ enable_basic_to_client_group_chat_room_migration=0 enable_simple_group_chat_message_state=0 aggregate_imdn=1 notify_each_friend_individually_when_presence_received=0 +store_friends=0 [app] activation_code_length=4 diff --git a/app/src/main/java/org/linphone/contacts/ContactLoader.kt b/app/src/main/java/org/linphone/contacts/ContactLoader.kt index 0520793fb..a8fe041ec 100644 --- a/app/src/main/java/org/linphone/contacts/ContactLoader.kt +++ b/app/src/main/java/org/linphone/contacts/ContactLoader.kt @@ -49,6 +49,11 @@ class ContactLoader : LoaderManager.LoaderCallbacks { ContactsContract.CommonDataKinds.Phone.LABEL, ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER ) + + private const val TAG = "[Contacts Loader]" + + private const val NATIVE_ADDRESS_BOOK_FRIEND_LIST = "Native address-book" + const val LINPHONE_ADDRESS_BOOK_FRIEND_LIST = "Linphone address-book" } @MainThread @@ -77,15 +82,15 @@ class ContactLoader : LoaderManager.LoaderCallbacks { @MainThread override fun onLoadFinished(loader: Loader, cursor: Cursor?) { if (cursor == null) { - Log.e("[Contacts Loader] Cursor is null!") + Log.e("$TAG Cursor is null!") return } - Log.i("[Contacts Loader] Load finished, found ${cursor.count} entries in cursor") + Log.i("$TAG Load finished, found ${cursor.count} entries in cursor") coreContext.postOnCoreThread { core -> val state = coreContext.core.globalState if (state == GlobalState.Shutdown || state == GlobalState.Off) { - Log.w("[Contacts Loader] Core is being stopped or already destroyed, abort") + Log.w("$TAG Core is being stopped or already destroyed, abort") return@postOnCoreThread } @@ -278,47 +283,57 @@ class ContactLoader : LoaderManager.LoaderCallbacks { friends[id] = friend } catch (e: Exception) { - Log.e("[Contacts Loader] Exception: $e") + Log.e("$TAG Exception: $e") } } if (core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off) { - Log.w("[Contacts Loader] Core is being stopped or already destroyed, abort") + Log.w("$TAG Core is being stopped or already destroyed, abort") } else if (friends.isEmpty()) { - Log.w("[Contacts Loader] No friend created!") + Log.w("$TAG No friend created!") } else { - Log.i("[Contacts Loader] ${friends.size} friends created") + Log.i("$TAG ${friends.size} friends created") - val fl = core.defaultFriendList ?: core.createFriendList() - for (friend in fl.friends) { - fl.removeFriend(friend) + val fl = core.getFriendListByName(NATIVE_ADDRESS_BOOK_FRIEND_LIST) ?: core.createFriendList() + if (fl.displayName.isNullOrEmpty()) { + Log.i( + "$TAG Friend list [$NATIVE_ADDRESS_BOOK_FRIEND_LIST] didn't exist yet, let's create it" + ) + fl.isDatabaseStorageEnabled = false // We don't want to store local address-book in DB + fl.displayName = NATIVE_ADDRESS_BOOK_FRIEND_LIST + core.addFriendList(fl) + } else { + Log.i( + "$TAG Friend list [$LINPHONE_ADDRESS_BOOK_FRIEND_LIST] found, removing existing friends if any" + ) + for (friend in fl.friends) { + fl.removeFriend(friend) + } } - if (fl != core.defaultFriendList) core.addFriendList(fl) - val friendsList = friends.values for (friend in friendsList) { fl.addLocalFriend(friend) } friends.clear() - Log.i("[Contacts Loader] Friends added") + Log.i("$TAG Friends added") fl.updateSubscriptions() - Log.i("[Contacts Loader] Subscription(s) updated") + Log.i("$TAG Subscription(s) updated") coreContext.contactsManager.onContactsLoaded() } } catch (sde: StaleDataException) { - Log.e("[Contacts Loader] State Data Exception: $sde") + Log.e("$TAG State Data Exception: $sde") } catch (ise: IllegalStateException) { - Log.e("[Contacts Loader] Illegal State Exception: $ise") + Log.e("$TAG Illegal State Exception: $ise") } catch (e: Exception) { - Log.e("[Contacts Loader] Exception: $e") + Log.e("$TAG Exception: $e") } } } @MainThread override fun onLoaderReset(loader: Loader) { - Log.i("[Contacts Loader] Loader reset") + Log.i("$TAG Loader reset") } } diff --git a/app/src/main/java/org/linphone/contacts/ContactsManager.kt b/app/src/main/java/org/linphone/contacts/ContactsManager.kt index 514c2a7f2..d9d5d4048 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsManager.kt +++ b/app/src/main/java/org/linphone/contacts/ContactsManager.kt @@ -117,7 +117,11 @@ class ContactsManager @UiThread constructor(context: Context) { @WorkerThread fun findContactById(id: String): Friend? { - return coreContext.core.defaultFriendList?.findFriendByRefKey(id) + for (friendList in coreContext.core.friendsLists) { + val found = friendList.findFriendByRefKey(id) + if (found != null) return found + } + return null } @WorkerThread diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index c41319892..0af11a69c 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -104,10 +104,11 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C coreThread = Handler(looper) core = Factory.instance().createCoreWithConfig(corePreferences.config, context) - core.isAutoIterateEnabled = false core.addListener(coreListener) + core.friendsDatabasePath = corePreferences.friendsDatabasePath + val timer = Timer("Linphone core.iterate() scheduler") timer.schedule( object : TimerTask() { diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index 013b5b4b2..224099d51 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -29,6 +29,10 @@ import java.io.FileOutputStream import org.linphone.LinphoneApplication.Companion.coreContext class CorePreferences @UiThread constructor(private val context: Context) { + companion object { + private const val TAG = "[Preferences]" + } + private var _config: Config? = null @get:WorkerThread @set:WorkerThread @@ -82,6 +86,10 @@ class CorePreferences @UiThread constructor(private val context: Context) { val factoryConfigPath: String get() = context.filesDir.absolutePath + "/linphonerc" + @get:AnyThread + val friendsDatabasePath: String + get() = context.filesDir.absolutePath + "/friends.db" + @get:AnyThread val linphoneDefaultValuesPath: String get() = context.filesDir.absolutePath + "/assistant_linphone_default_values" @@ -100,14 +108,14 @@ class CorePreferences @UiThread constructor(private val context: Context) { if (!overrideIfExists) { android.util.Log.i( context.getString(org.linphone.R.string.app_name), - "[Preferences] File $to already exists" + "$TAG File $to already exists" ) return } } android.util.Log.i( context.getString(org.linphone.R.string.app_name), - "[Preferences] Overriding $to by $from asset" + "$TAG Overriding $to by $from asset" ) val outStream = FileOutputStream(outFile) 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 358d4691c..575e9b7d2 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 @@ -106,12 +106,15 @@ class ContactsListFragment : GenericFragment() { listViewModel.contactsList.observe( viewLifecycleOwner ) { + val emptyAdapter = adapter.itemCount == 0 adapter.submitList(it) - Log.i("$TAG Contacts list is ready with [${it.size}] items") + Log.i("$TAG Contacts list updated with [${it.size}] items") - (view.parent as? ViewGroup)?.doOnPreDraw { - startPostponedEnterTransition() - sharedViewModel.contactsListReadyToBeDisplayedEvent.value = Event(true) + if (emptyAdapter) { + (view.parent as? ViewGroup)?.doOnPreDraw { + startPostponedEnterTransition() + sharedViewModel.contactsListReadyToBeDisplayedEvent.value = Event(true) + } } } @@ -119,7 +122,7 @@ class ContactsListFragment : GenericFragment() { viewLifecycleOwner ) { favouritesAdapter.submitList(it) - Log.i("$TAG Favourites contacts list is ready with [${it.size}] items") + Log.i("$TAG Favourites contacts list updated with [${it.size}] items") } listViewModel.vCardTerminatedEvent.observe(viewLifecycleOwner) { diff --git a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactNewOrEditViewModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactNewOrEditViewModel.kt index 5a899f122..cc54f7c13 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactNewOrEditViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/viewmodel/ContactNewOrEditViewModel.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.contacts.ContactLoader.Companion.LINPHONE_ADDRESS_BOOK_FRIEND_LIST import org.linphone.core.Friend import org.linphone.core.FriendList.Status import org.linphone.core.tools.Log @@ -179,10 +180,23 @@ class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() { Log.e("$TAG Failed to generate a ref key using vCard's generateUniqueId()") // TODO : generate unique ref key } - status = core.defaultFriendList?.addFriend(friend) ?: Status.InvalidFriend + friend.done() + + val fl = core.getFriendListByName(LINPHONE_ADDRESS_BOOK_FRIEND_LIST) ?: core.createFriendList() + if (fl.displayName.isNullOrEmpty()) { + Log.i( + "$TAG Locally saved friend list [$LINPHONE_ADDRESS_BOOK_FRIEND_LIST] didn't exist yet, let's create it" + ) + fl.isDatabaseStorageEnabled = true // We do want to store friends created in app in DB + fl.displayName = LINPHONE_ADDRESS_BOOK_FRIEND_LIST + core.addFriendList(fl) + } + status = fl.addFriend(friend) + fl.updateSubscriptions() + } else { + friend.done() } - friend.done() coreContext.contactsManager.notifyContactsListChanged() saveChangesEvent.postValue( diff --git a/app/src/main/res/layout/contact_new_or_edit_fragment.xml b/app/src/main/res/layout/contact_new_or_edit_fragment.xml index fc1166843..f05bd52ec 100644 --- a/app/src/main/res/layout/contact_new_or_edit_fragment.xml +++ b/app/src/main/res/layout/contact_new_or_edit_fragment.xml @@ -142,7 +142,7 @@ android:textColor="@color/gray_9" android:maxLines="1" android:background="@drawable/edit_text_background" - android:inputType="text|textPersonName" + android:inputType="text|textPersonName|textCapWords" app:layout_constraintTop_toBottomOf="@id/first_name_label" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> @@ -176,7 +176,7 @@ android:textColor="@color/gray_9" android:maxLines="1" android:background="@drawable/edit_text_background" - android:inputType="text|textPersonName" + android:inputType="text|textPersonName|textCapWords" app:layout_constraintTop_toBottomOf="@id/last_name_label" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> @@ -230,7 +230,7 @@ android:textColor="@color/gray_9" android:maxLines="1" android:background="@drawable/edit_text_background" - android:inputType="text" + android:inputType="text|textCapWords" app:layout_constraintTop_toBottomOf="@id/company_label" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> @@ -265,7 +265,7 @@ android:textColor="@color/gray_9" android:maxLines="1" android:background="@drawable/edit_text_background" - android:inputType="text" + android:inputType="text|textCapWords" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@id/job_title_label" app:layout_constraintStart_toStartOf="parent"