Made accounts switch work

This commit is contained in:
Sylvain Berfini 2023-09-06 16:10:03 +02:00
parent 36b5430861
commit 29a47b87cd
13 changed files with 226 additions and 156 deletions

View file

@ -33,14 +33,4 @@
<section name="net">
<entry name="friendlist_subscription_enabled" overwrite="true">1</entry>
</section>
<section name="assistant">
<entry name="domain" overwrite="true">sip.linphone.org</entry>
<entry name="algorithm" overwrite="true">SHA-256</entry>
<entry name="password_max_length" overwrite="true">-1</entry>
<entry name="password_min_length" overwrite="true">1</entry>
<entry name="username_length" overwrite="true">-1</entry>
<entry name="username_max_length" overwrite="true">64</entry>
<entry name="username_min_length" overwrite="true">1</entry>
<entry name="username_regex" overwrite="true">^[a-z0-9+_.\-]*$</entry>
</section>
</config>

View file

@ -22,21 +22,4 @@
<entry name="rtp_bundle" overwrite="true">0</entry>
<entry name="lime_server_url" overwrite="true"></entry>
</section>
<section name="nat_policy_default_values">
<entry name="stun_server" overwrite="true"></entry>
<entry name="protocols" overwrite="true"></entry>
</section>
<section name="net">
<entry name="friendlist_subscription_enabled" overwrite="true">0</entry>
</section>
<section name="assistant">
<entry name="domain" overwrite="true"></entry>
<entry name="algorithm" overwrite="true">MD5</entry>
<entry name="password_max_length" overwrite="true">-1</entry>
<entry name="password_min_length" overwrite="true">0</entry>
<entry name="username_length" overwrite="true">-1</entry>
<entry name="username_max_length" overwrite="true">128</entry>
<entry name="username_min_length" overwrite="true">1</entry>
<entry name="username_regex" overwrite="true">^[a-zA-Z0-9+_.\-]*$</entry>
</section>
</config>

View file

@ -31,7 +31,6 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.navGraphViewModels
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.linphone.LinphoneApplication
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.tools.Log

View file

@ -25,27 +25,18 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.PopupWindow
import androidx.annotation.DrawableRes
import androidx.annotation.UiThread
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.findNavController
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.Account
import org.linphone.databinding.AccountPopupMenuBinding
import org.linphone.databinding.MainActivityBinding
import org.linphone.ui.assistant.AssistantActivity
import org.linphone.ui.main.settings.fragment.AccountProfileFragmentDirections
import org.linphone.ui.main.viewmodel.DrawerMenuViewModel
import org.linphone.ui.welcome.WelcomeActivity
import org.linphone.utils.slideInToastFromTopForDuration
@ -60,7 +51,6 @@ class MainActivity : AppCompatActivity() {
}
private lateinit var binding: MainActivityBinding
private lateinit var drawerMenuViewModel: DrawerMenuViewModel
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, true)
@ -78,48 +68,6 @@ class MainActivity : AppCompatActivity() {
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
binding.lifecycleOwner = this
binding.setSettingsClickedListener {
val navController = findNavController(R.id.main_nav_host_fragment)
navController.navigate(R.id.action_global_settingsFragment)
binding.drawerMenu.close()
}
binding.setRecordingsClickListener {
val navController = findNavController(R.id.main_nav_host_fragment)
navController.navigate(R.id.action_global_recordingsFragment)
binding.drawerMenu.close()
}
binding.setHelpClickedListener {
val navController = findNavController(R.id.main_nav_host_fragment)
navController.navigate(R.id.action_global_helpFragment)
binding.drawerMenu.close()
}
drawerMenuViewModel = run {
ViewModelProvider(this)[DrawerMenuViewModel::class.java]
}
binding.drawerMenuViewModel = drawerMenuViewModel
drawerMenuViewModel.startAssistantEvent.observe(this) {
it.consume {
startActivity(Intent(baseContext, AssistantActivity::class.java))
binding.drawerMenu.close()
}
}
drawerMenuViewModel.closeDrawerEvent.observe(this) {
it.consume {
binding.drawerMenu.close()
}
}
drawerMenuViewModel.showAccountPopupMenuEvent.observe(this) {
it.consume { pair ->
showAccountPopupMenu(pair.first, pair.second)
}
}
}
override fun onPostCreate(savedInstanceState: Bundle?) {
@ -174,12 +122,20 @@ class MainActivity : AppCompatActivity() {
@SuppressLint("RtlHardcoded")
fun toggleDrawerMenu() {
if (binding.drawerMenu.isDrawerOpen(Gravity.LEFT)) {
binding.drawerMenu.closeDrawer(binding.drawerMenuContent, true)
closeDrawerMenu()
} else {
binding.drawerMenu.openDrawer(binding.drawerMenuContent, true)
}
}
fun closeDrawerMenu() {
binding.drawerMenu.closeDrawer(binding.drawerMenuContent, true)
}
fun findNavController(): NavController {
return findNavController(R.id.main_nav_host_fragment)
}
fun showGreenToast(message: String, @DrawableRes icon: Int) {
binding.greenToast.message = message
binding.greenToast.icon = icon
@ -211,35 +167,4 @@ class MainActivity : AppCompatActivity() {
}
}*/
}
private fun showAccountPopupMenu(view: View, account: Account) {
val popupView: AccountPopupMenuBinding = DataBindingUtil.inflate(
LayoutInflater.from(baseContext),
R.layout.account_popup_menu,
null,
false
)
val popupWindow = PopupWindow(
popupView.root,
WRAP_CONTENT,
WRAP_CONTENT,
true
)
popupView.setManageProfileClickListener {
val navController = findNavController(R.id.main_nav_host_fragment)
val identity = account.params.identityAddress?.asStringUriOnly().orEmpty()
val action = AccountProfileFragmentDirections.actionGlobalAccountProfileFragment(
identity
)
navController.navigate(action)
popupWindow.dismiss()
binding.drawerMenu.close()
}
// Elevation is for showing a shadow around the popup
popupWindow.elevation = 20f
popupWindow.showAsDropDown(view, 0, 0, Gravity.BOTTOM)
}
}

View file

@ -189,6 +189,16 @@ class CallsListFragment : GenericFragment() {
findNavController().navigate(R.id.action_global_startCallFragment)
}
sharedViewModel.defaultAccountChangedEvent.observe(viewLifecycleOwner) {
it.consume {
Log.i(
"$TAG Default account changed, updating avatar in top bar & re-computing call logs"
)
listViewModel.updateDefaultAccount()
listViewModel.applyFilter()
}
}
// TopBarFragment related
listViewModel.openDrawerMenuEvent.observe(viewLifecycleOwner) {

View file

@ -148,6 +148,16 @@ class ContactsListFragment : GenericFragment() {
listViewModel.toggleContactsFilter()
}
sharedViewModel.defaultAccountChangedEvent.observe(viewLifecycleOwner) {
it.consume {
Log.i(
"$TAG Default account changed, updating avatar in top bar & refreshing contacts list"
)
listViewModel.updateDefaultAccount()
listViewModel.applyFilter()
}
}
// TopBarFragment related
listViewModel.openDrawerMenuEvent.observe(viewLifecycleOwner) {

View file

@ -164,7 +164,7 @@ class ContactsListViewModel @UiThread constructor() : AbstractTopBarViewModel()
}
@UiThread
fun applyFilter(filter: String) {
fun applyFilter(filter: String = currentFilter) {
isListFiltered.value = filter.isNotEmpty()
coreContext.postOnCoreThread {
applyFilter(

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2010-2023 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.ui.main.fragment
import android.content.Intent
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import org.linphone.R
import org.linphone.core.Account
import org.linphone.core.tools.Log
import org.linphone.databinding.AccountPopupMenuBinding
import org.linphone.databinding.DrawerMenuBinding
import org.linphone.ui.assistant.AssistantActivity
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.settings.fragment.AccountProfileFragmentDirections
import org.linphone.ui.main.viewmodel.DrawerMenuViewModel
import org.linphone.utils.Event
@UiThread
class DrawerMenuFragment : GenericFragment() {
companion object {
private const val TAG = "[Drawer Menu Fragment]"
}
private lateinit var binding: DrawerMenuBinding
private lateinit var viewModel: DrawerMenuViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DrawerMenuBinding.inflate(layoutInflater)
return binding.root
}
override fun goBack() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = requireActivity().run {
ViewModelProvider(this)[DrawerMenuViewModel::class.java]
}
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
viewModel.startAssistantEvent.observe(viewLifecycleOwner) {
it.consume {
startActivity(Intent(requireContext(), AssistantActivity::class.java))
(requireActivity() as MainActivity).closeDrawerMenu()
}
}
viewModel.closeDrawerEvent.observe(viewLifecycleOwner) {
it.consume {
(requireActivity() as MainActivity).closeDrawerMenu()
}
}
viewModel.showAccountPopupMenuEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
showAccountPopupMenu(pair.first, pair.second)
}
}
viewModel.defaultAccountChangedEvent.observe(viewLifecycleOwner) {
it.consume { identity ->
Log.w("$TAG Default account has changed, now is [$identity]")
sharedViewModel.defaultAccountChangedEvent.value = Event(true)
}
}
binding.setSettingsClickedListener {
val navController = (requireActivity() as MainActivity).findNavController()
navController.navigate(R.id.action_global_settingsFragment)
(requireActivity() as MainActivity).closeDrawerMenu()
}
binding.setRecordingsClickListener {
val navController = (requireActivity() as MainActivity).findNavController()
navController.navigate(R.id.action_global_recordingsFragment)
(requireActivity() as MainActivity).closeDrawerMenu()
}
binding.setHelpClickedListener {
val navController = (requireActivity() as MainActivity).findNavController()
navController.navigate(R.id.action_global_helpFragment)
(requireActivity() as MainActivity).closeDrawerMenu()
}
}
private fun showAccountPopupMenu(view: View, account: Account) {
val popupView: AccountPopupMenuBinding = DataBindingUtil.inflate(
LayoutInflater.from(requireContext()),
R.layout.account_popup_menu,
null,
false
)
val popupWindow = PopupWindow(
popupView.root,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
true
)
popupView.setManageProfileClickListener {
val navController = (requireActivity() as MainActivity).findNavController()
val identity = account.params.identityAddress?.asStringUriOnly().orEmpty()
val action = AccountProfileFragmentDirections.actionGlobalAccountProfileFragment(
identity
)
Log.i("$TAG Going to account [$identity] profile")
navController.navigate(action)
popupWindow.dismiss()
(requireActivity() as MainActivity).closeDrawerMenu()
}
// Elevation is for showing a shadow around the popup
popupWindow.elevation = 20f
popupWindow.showAsDropDown(view, 0, 0, Gravity.BOTTOM)
}
}

View file

@ -31,8 +31,9 @@ import org.linphone.core.tools.Log
import org.linphone.utils.LinphoneUtils
class AccountModel @WorkerThread constructor(
private val account: Account,
private val onMenuClicked: ((view: View, account: Account) -> Unit)? = null
val account: Account,
private val onMenuClicked: ((view: View, account: Account) -> Unit)? = null,
private val onSetAsDefault: ((account: Account) -> Unit)? = null
) {
companion object {
private const val TAG = "[Account Model]"
@ -85,8 +86,9 @@ class AccountModel @WorkerThread constructor(
fun setAsDefault() {
coreContext.postOnCoreThread { core ->
core.defaultAccount = account
isDefault.postValue(true)
}
isDefault.value = true
onSetAsDefault?.invoke(account)
}
@UiThread
@ -135,5 +137,5 @@ class AccountModel @WorkerThread constructor(
fun Account.isInSecureMode(): Boolean {
// TODO FIXME
return true
return params.identityAddress?.domain == "sip.linphone.org"
}

View file

@ -20,7 +20,6 @@
package org.linphone.ui.main.viewmodel
import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
@ -52,11 +51,7 @@ open class AbstractTopBarViewModel @UiThread constructor() : ViewModel() {
init {
searchBarVisible.value = false
// TODO FIXME: update default account displayed here when uses clicks on another account
coreContext.postOnCoreThread {
updateDefaultAccount()
}
updateDefaultAccount()
}
@UiThread
@ -91,14 +86,16 @@ open class AbstractTopBarViewModel @UiThread constructor() : ViewModel() {
searchFilter.value = ""
}
@WorkerThread
private fun updateDefaultAccount() {
Log.i("$TAG Updating displayed default account")
@UiThread
fun updateDefaultAccount() {
coreContext.postOnCoreThread {
Log.i("$TAG Updating displayed default account")
val core = coreContext.core
if (core.accountList.isNotEmpty()) {
val defaultAccount = core.defaultAccount ?: core.accountList.first()
account.postValue(AccountModel(defaultAccount))
val core = coreContext.core
if (core.accountList.isNotEmpty()) {
val defaultAccount = core.defaultAccount ?: core.accountList.first()
account.postValue(AccountModel(defaultAccount))
}
}
}
}

View file

@ -49,6 +49,10 @@ class DrawerMenuViewModel @UiThread constructor() : ViewModel() {
MutableLiveData<Event<Pair<View, Account>>>()
}
val defaultAccountChangedEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
init {
// TODO FIXME: update accounts list when a new account is added or when removing one
coreContext.postOnCoreThread {
@ -80,9 +84,23 @@ class DrawerMenuViewModel @UiThread constructor() : ViewModel() {
val list = arrayListOf<AccountModel>()
for (account in coreContext.core.accountList) {
val model = AccountModel(account) { view, account ->
val model = AccountModel(account, { view, account ->
// onClicked
showAccountPopupMenuEvent.postValue(Event(Pair(view, account)))
}
}, { account ->
// onSetAsDefault
Log.i(
"$TAG Account [${account.params.identityAddress?.asStringUriOnly()}] has been set as default by user"
)
for (model in accounts.value.orEmpty()) {
if (model.account != account) {
model.isDefault.value = false
}
}
defaultAccountChangedEvent.postValue(
Event(account.params.identityAddress?.asStringUriOnly() ?: "")
)
})
list.add(model)
}
accounts.postValue(list)

View file

@ -57,6 +57,10 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
MutableLiveData<Event<String>>()
}
val defaultAccountChangedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
/* Contacts related */
val contactsListReadyToBeDisplayedEvent = MutableLiveData<Event<Boolean>>()

View file

@ -5,18 +5,6 @@
<data>
<import type="android.view.View" />
<variable
name="settingsClickedListener"
type="View.OnClickListener" />
<variable
name="recordingsClickListener"
type="View.OnClickListener" />
<variable
name="helpClickedListener"
type="View.OnClickListener" />
<variable
name="drawerMenuViewModel"
type="org.linphone.ui.main.viewmodel.DrawerMenuViewModel" />
</data>
<androidx.drawerlayout.widget.DrawerLayout
@ -59,21 +47,14 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Side Menu -->
<RelativeLayout
<androidx.fragment.app.FragmentContainerView
android:id="@+id/drawer_menu_content"
android:name="org.linphone.ui.main.fragment.DrawerMenuFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:layout_gravity="start">
<include
tools:settingsClickedListener="@{settingsClickedListener}"
tools:recordingsClickListener="@{recordingsClickListener}"
tools:helpClickedListener="@{helpClickedListener}"
tools:viewModel="@{drawerMenuViewModel}"
layout="@layout/drawer_menu" />
</RelativeLayout>
android:layout_gravity="start"
app:layout="@layout/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>