diff --git a/app/src/main/java/org/linphone/ui/assistant/fragment/LandingFragment.kt b/app/src/main/java/org/linphone/ui/assistant/fragment/LandingFragment.kt
new file mode 100644
index 000000000..d2b2dc226
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/assistant/fragment/LandingFragment.kt
@@ -0,0 +1,190 @@
+/*
+ * 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 .
+ */
+package org.linphone.ui.assistant.fragment
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.UiThread
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.navGraphViewModels
+import org.linphone.LinphoneApplication.Companion.coreContext
+import org.linphone.LinphoneApplication.Companion.corePreferences
+import org.linphone.R
+import org.linphone.core.tools.Log
+import org.linphone.databinding.AssistantLandingFragmentBinding
+import org.linphone.ui.assistant.model.AcceptConditionsAndPolicyDialogModel
+import org.linphone.ui.assistant.viewmodel.LandingViewModel
+import org.linphone.ui.sso.OpenIdActivity
+import org.linphone.utils.DialogUtils
+
+@UiThread
+class LandingFragment : Fragment() {
+ companion object {
+ private const val TAG = "[Landing Fragment]"
+ }
+
+ private lateinit var binding: AssistantLandingFragmentBinding
+
+ private val viewModel: LandingViewModel by navGraphViewModels(
+ R.id.assistant_nav_graph
+ )
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = AssistantLandingFragmentBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.lifecycleOwner = viewLifecycleOwner
+ binding.viewModel = viewModel
+
+ binding.setBackClickListener {
+ requireActivity().finish()
+ }
+
+ binding.setRegisterClickListener {
+ if (viewModel.conditionsAndPrivacyPolicyAccepted) {
+ goToRegisterFragment()
+ } else {
+ showAcceptConditionsAndPrivacyDialog(goToAccountCreate = true)
+ }
+ }
+
+ binding.setQrCodeClickListener {
+ val action = LandingFragmentDirections.actionLandingFragmentToQrCodeScannerFragment()
+ findNavController().navigate(action)
+ }
+
+ binding.setThirdPartySipAccountLoginClickListener {
+ if (viewModel.conditionsAndPrivacyPolicyAccepted) {
+ goToLoginThirdPartySipAccountFragment()
+ } else {
+ showAcceptConditionsAndPrivacyDialog(goToThirdPartySipAccountLogin = true)
+ }
+ }
+
+ viewModel.redirectToDigestAuthEvent.observe(viewLifecycleOwner) {
+ it.consume {
+ goToLoginFragment()
+ }
+ }
+
+ viewModel.redirectToSingleSignOnEvent.observe(viewLifecycleOwner) {
+ it.consume {
+ goToSingleSignOnActivity()
+ }
+ }
+ }
+
+ private fun goToLoginFragment() {
+ val identity = viewModel.sipIdentity.value.orEmpty()
+ val action = LandingFragmentDirections.actionLandingFragmentToLoginFragment(identity)
+ findNavController().navigate(action)
+ }
+
+ private fun goToSingleSignOnActivity() {
+ startActivity(Intent(requireContext(), OpenIdActivity::class.java))
+ requireActivity().finish()
+ }
+
+ private fun goToRegisterFragment() {
+ val action = LandingFragmentDirections.actionLandingFragmentToRegisterFragment()
+ findNavController().navigate(action)
+ }
+
+ private fun goToLoginThirdPartySipAccountFragment() {
+ val action = LandingFragmentDirections.actionLandingFragmentToThirdPartySipAccountWarningFragment()
+ findNavController().navigate(action)
+ }
+
+ private fun showAcceptConditionsAndPrivacyDialog(
+ goToAccountCreate: Boolean = false,
+ goToThirdPartySipAccountLogin: Boolean = false
+ ) {
+ val model = AcceptConditionsAndPolicyDialogModel()
+ val dialog = DialogUtils.getAcceptConditionsAndPrivacyDialog(
+ requireActivity(),
+ model
+ )
+
+ model.dismissEvent.observe(viewLifecycleOwner) {
+ it.consume {
+ dialog.dismiss()
+ }
+ }
+
+ model.conditionsAcceptedEvent.observe(viewLifecycleOwner) {
+ it.consume {
+ Log.i("$TAG Conditions & Privacy policy have been accepted")
+ coreContext.postOnCoreThread {
+ corePreferences.conditionsAndPrivacyPolicyAccepted = true
+ }
+ dialog.dismiss()
+
+ if (goToAccountCreate) {
+ goToRegisterFragment()
+ } else if (goToThirdPartySipAccountLogin) {
+ goToLoginThirdPartySipAccountFragment()
+ }
+ }
+ }
+
+ model.privacyPolicyClickedEvent.observe(viewLifecycleOwner) {
+ it.consume {
+ val url = getString(R.string.website_privacy_policy_url)
+ try {
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ startActivity(browserIntent)
+ } catch (ise: IllegalStateException) {
+ Log.e(
+ "$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
+ )
+ }
+ }
+ }
+
+ model.generalTermsClickedEvent.observe(viewLifecycleOwner) {
+ it.consume {
+ val url = getString(R.string.website_terms_and_conditions_url)
+ try {
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ startActivity(browserIntent)
+ } catch (ise: IllegalStateException) {
+ Log.e(
+ "$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
+ )
+ }
+ }
+ }
+
+ dialog.show()
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/assistant/fragment/LoginFragment.kt b/app/src/main/java/org/linphone/ui/assistant/fragment/LoginFragment.kt
index f651242a9..fcc49e127 100644
--- a/app/src/main/java/org/linphone/ui/assistant/fragment/LoginFragment.kt
+++ b/app/src/main/java/org/linphone/ui/assistant/fragment/LoginFragment.kt
@@ -29,19 +29,16 @@ import androidx.annotation.UiThread
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
import androidx.navigation.navGraphViewModels
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.linphone.LinphoneApplication.Companion.coreContext
-import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.AssistantLoginFragmentBinding
import org.linphone.ui.assistant.AssistantActivity
-import org.linphone.ui.assistant.model.AcceptConditionsAndPolicyDialogModel
import org.linphone.ui.assistant.viewmodel.AccountLoginViewModel
-import org.linphone.ui.sso.OpenIdActivity
-import org.linphone.utils.DialogUtils
import org.linphone.utils.PhoneNumberUtils
@UiThread
@@ -52,6 +49,8 @@ class LoginFragment : Fragment() {
private lateinit var binding: AssistantLoginFragmentBinding
+ private val args: LoginFragmentArgs by navArgs()
+
private val viewModel: AccountLoginViewModel by navGraphViewModels(
R.id.assistant_nav_graph
)
@@ -72,16 +71,11 @@ class LoginFragment : Fragment() {
binding.viewModel = viewModel
binding.setBackClickListener {
- requireActivity().finish()
+ goBack()
}
- binding.setRegisterClickListener {
- if (viewModel.conditionsAndPrivacyPolicyAccepted) {
- goToRegisterFragment()
- } else {
- showAcceptConditionsAndPrivacyDialog(goToAccountCreate = true)
- }
- }
+ val identity = args.sipIdentity
+ viewModel.sipIdentity.value = identity
binding.setForgottenPasswordClickListener {
val url = getString(R.string.web_platform_forgotten_password_url)
@@ -95,24 +89,6 @@ class LoginFragment : Fragment() {
}
}
- binding.setSingleSignOnClickListener {
- startActivity(Intent(requireContext(), OpenIdActivity::class.java))
- requireActivity().finish()
- }
-
- binding.setQrCodeClickListener {
- val action = LoginFragmentDirections.actionLoginFragmentToQrCodeScannerFragment()
- findNavController().navigate(action)
- }
-
- binding.setThirdPartySipAccountLoginClickListener {
- if (viewModel.conditionsAndPrivacyPolicyAccepted) {
- goToLoginThirdPartySipAccountFragment()
- } else {
- showAcceptConditionsAndPrivacyDialog(goToThirdPartySipAccountLogin = true)
- }
- }
-
viewModel.showPassword.observe(viewLifecycleOwner) {
lifecycleScope.launch {
delay(50)
@@ -146,76 +122,7 @@ class LoginFragment : Fragment() {
}
}
- private fun goToRegisterFragment() {
- val action = LoginFragmentDirections.actionLoginFragmentToRegisterFragment()
- findNavController().navigate(action)
- }
-
- private fun goToLoginThirdPartySipAccountFragment() {
- val action = LoginFragmentDirections.actionLoginFragmentToThirdPartySipAccountWarningFragment()
- findNavController().navigate(action)
- }
-
- private fun showAcceptConditionsAndPrivacyDialog(
- goToAccountCreate: Boolean = false,
- goToThirdPartySipAccountLogin: Boolean = false
- ) {
- val model = AcceptConditionsAndPolicyDialogModel()
- val dialog = DialogUtils.getAcceptConditionsAndPrivacyDialog(
- requireActivity(),
- model
- )
-
- model.dismissEvent.observe(viewLifecycleOwner) {
- it.consume {
- dialog.dismiss()
- }
- }
-
- model.conditionsAcceptedEvent.observe(viewLifecycleOwner) {
- it.consume {
- Log.i("$TAG Conditions & Privacy policy have been accepted")
- coreContext.postOnCoreThread {
- corePreferences.conditionsAndPrivacyPolicyAccepted = true
- }
- dialog.dismiss()
-
- if (goToAccountCreate) {
- goToRegisterFragment()
- } else if (goToThirdPartySipAccountLogin) {
- goToLoginThirdPartySipAccountFragment()
- }
- }
- }
-
- model.privacyPolicyClickedEvent.observe(viewLifecycleOwner) {
- it.consume {
- val url = getString(R.string.website_privacy_policy_url)
- try {
- val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
- startActivity(browserIntent)
- } catch (ise: IllegalStateException) {
- Log.e(
- "$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
- )
- }
- }
- }
-
- model.generalTermsClickedEvent.observe(viewLifecycleOwner) {
- it.consume {
- val url = getString(R.string.website_terms_and_conditions_url)
- try {
- val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
- startActivity(browserIntent)
- } catch (ise: IllegalStateException) {
- Log.e(
- "$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
- )
- }
- }
- }
-
- dialog.show()
+ private fun goBack() {
+ findNavController().popBackStack()
}
}
diff --git a/app/src/main/java/org/linphone/ui/assistant/fragment/PermissionsFragment.kt b/app/src/main/java/org/linphone/ui/assistant/fragment/PermissionsFragment.kt
index 32333f268..3f6ffb823 100644
--- a/app/src/main/java/org/linphone/ui/assistant/fragment/PermissionsFragment.kt
+++ b/app/src/main/java/org/linphone/ui/assistant/fragment/PermissionsFragment.kt
@@ -113,7 +113,7 @@ class PermissionsFragment : Fragment() {
}
private fun goToLoginFragment() {
- val action = PermissionsFragmentDirections.actionPermissionsFragmentToLoginFragment()
+ val action = PermissionsFragmentDirections.actionPermissionsFragmentToLandingFragment()
findNavController().navigate(action)
}
diff --git a/app/src/main/java/org/linphone/ui/assistant/fragment/RegisterCodeConfirmationFragment.kt b/app/src/main/java/org/linphone/ui/assistant/fragment/RegisterCodeConfirmationFragment.kt
index a77590d25..d73d86141 100644
--- a/app/src/main/java/org/linphone/ui/assistant/fragment/RegisterCodeConfirmationFragment.kt
+++ b/app/src/main/java/org/linphone/ui/assistant/fragment/RegisterCodeConfirmationFragment.kt
@@ -68,7 +68,10 @@ class RegisterCodeConfirmationFragment : Fragment() {
viewModel.goToLoginPageEvent.observe(viewLifecycleOwner) {
it.consume {
Log.i("$TAG Going to login fragment")
- val action = RegisterCodeConfirmationFragmentDirections.actionRegisterCodeConfirmationFragmentToLoginFragment()
+ val identity = viewModel.username.value.orEmpty()
+ val action = RegisterCodeConfirmationFragmentDirections.actionRegisterCodeConfirmationFragmentToLoginFragment(
+ identity
+ )
findNavController().navigate(action)
}
}
diff --git a/app/src/main/java/org/linphone/ui/assistant/viewmodel/AccountLoginViewModel.kt b/app/src/main/java/org/linphone/ui/assistant/viewmodel/AccountLoginViewModel.kt
index 19f034013..cb93e9e30 100644
--- a/app/src/main/java/org/linphone/ui/assistant/viewmodel/AccountLoginViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/assistant/viewmodel/AccountLoginViewModel.kt
@@ -43,9 +43,7 @@ class AccountLoginViewModel @UiThread constructor() : ViewModel() {
private const val TAG = "[Account Login ViewModel]"
}
- val showBackButton = MutableLiveData()
-
- val username = MutableLiveData()
+ val sipIdentity = MutableLiveData()
val password = MutableLiveData()
@@ -69,8 +67,6 @@ class AccountLoginViewModel @UiThread constructor() : ViewModel() {
MutableLiveData>()
}
- var conditionsAndPrivacyPolicyAccepted = false
-
private lateinit var newlyCreatedAuthInfo: AuthInfo
private lateinit var newlyCreatedAccount: Account
@@ -121,13 +117,7 @@ class AccountLoginViewModel @UiThread constructor() : ViewModel() {
showPassword.value = false
registrationInProgress.value = false
- coreContext.postOnCoreThread { core ->
- // Prevent user from leaving assistant if no account was configured yet
- showBackButton.postValue(core.accountList.isNotEmpty())
- conditionsAndPrivacyPolicyAccepted = corePreferences.conditionsAndPrivacyPolicyAccepted
- }
-
- loginEnabled.addSource(username) {
+ loginEnabled.addSource(sipIdentity) {
loginEnabled.value = isLoginButtonEnabled()
}
loginEnabled.addSource(password) {
@@ -142,8 +132,14 @@ class AccountLoginViewModel @UiThread constructor() : ViewModel() {
coreContext.postOnCoreThread { core ->
core.loadConfigFromXml(corePreferences.linphoneDefaultValuesPath)
- val user = username.value.orEmpty().trim()
- val domain = corePreferences.defaultDomain
+ val identity = sipIdentity.value.orEmpty().trim()
+ val identityAddress = core.interpretUrl(identity, false)
+ identityAddress ?: return@postOnCoreThread
+
+ val user = identityAddress.username
+ user ?: return@postOnCoreThread
+
+ val domain = identityAddress.domain
newlyCreatedAuthInfo = Factory.instance().createAuthInfo(
user,
@@ -156,20 +152,6 @@ class AccountLoginViewModel @UiThread constructor() : ViewModel() {
core.addAuthInfo(newlyCreatedAuthInfo)
val accountParams = core.createAccountParams()
- val identity = if (user.startsWith("sip:")) {
- if (user.contains("@")) {
- user
- } else {
- "$user@$domain"
- }
- } else {
- if (user.contains("@")) {
- "sip:$user"
- } else {
- "sip:$user@$domain"
- }
- }
- val identityAddress = Factory.instance().createAddress(identity)
accountParams.identityAddress = identityAddress
val prefix = internationalPrefix.value.orEmpty().trim()
@@ -214,6 +196,6 @@ class AccountLoginViewModel @UiThread constructor() : ViewModel() {
@UiThread
private fun isLoginButtonEnabled(): Boolean {
- return username.value.orEmpty().isNotEmpty() && password.value.orEmpty().isNotEmpty()
+ return sipIdentity.value.orEmpty().isNotEmpty() && password.value.orEmpty().isNotEmpty()
}
}
diff --git a/app/src/main/java/org/linphone/ui/assistant/viewmodel/LandingViewModel.kt b/app/src/main/java/org/linphone/ui/assistant/viewmodel/LandingViewModel.kt
new file mode 100644
index 000000000..4e70cf455
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/assistant/viewmodel/LandingViewModel.kt
@@ -0,0 +1,81 @@
+/*
+ * 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 .
+ */
+package org.linphone.ui.assistant.viewmodel
+
+import androidx.annotation.UiThread
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import org.linphone.LinphoneApplication.Companion.coreContext
+import org.linphone.LinphoneApplication.Companion.corePreferences
+import org.linphone.core.Factory
+import org.linphone.utils.Event
+
+class LandingViewModel @UiThread constructor() : ViewModel() {
+ companion object {
+ private const val TAG = "[Account Login ViewModel]"
+ }
+
+ val showBackButton = MutableLiveData()
+
+ val sipIdentity = MutableLiveData()
+
+ val redirectToDigestAuthEvent: MutableLiveData> by lazy {
+ MutableLiveData>()
+ }
+
+ val redirectToSingleSignOnEvent: MutableLiveData> by lazy {
+ MutableLiveData>()
+ }
+
+ var conditionsAndPrivacyPolicyAccepted = false
+
+ init {
+ coreContext.postOnCoreThread { core ->
+ // Prevent user from leaving assistant if no account was configured yet
+ showBackButton.postValue(core.accountList.isNotEmpty())
+ conditionsAndPrivacyPolicyAccepted = corePreferences.conditionsAndPrivacyPolicyAccepted
+ }
+ }
+
+ @UiThread
+ fun login() {
+ coreContext.postOnCoreThread { core ->
+ var identity = sipIdentity.value.orEmpty()
+ if (!identity.startsWith("sip:")) {
+ identity = "sip:$identity"
+ }
+ if (!identity.contains("@")) {
+ identity = "$identity@${corePreferences.defaultDomain}"
+ }
+ val identityAddress = Factory.instance().createAddress(identity)
+ if (identityAddress == null) {
+ // TODO: FIXME: show error
+ return@postOnCoreThread
+ }
+
+ // TODO: SSO or password auth?
+ if (identityAddress.domain == corePreferences.defaultDomain) {
+ redirectToDigestAuthEvent.postValue(Event(true))
+ } else {
+ redirectToSingleSignOnEvent.postValue(Event(true))
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/layout/assistant_landing_fragment.xml b/app/src/main/res/layout/assistant_landing_fragment.xml
new file mode 100644
index 000000000..8abba59d0
--- /dev/null
+++ b/app/src/main/res/layout/assistant_landing_fragment.xml
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/assistant_login_fragment.xml b/app/src/main/res/layout/assistant_login_fragment.xml
index cf8945bb3..66ada8d80 100644
--- a/app/src/main/res/layout/assistant_login_fragment.xml
+++ b/app/src/main/res/layout/assistant_login_fragment.xml
@@ -8,21 +8,9 @@
-
-
-
-
@@ -46,7 +34,6 @@
android:padding="15dp"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_go_back_icon"
- android:visibility="@{viewModel.showBackButton ? View.VISIBLE : View.INVISIBLE, default=invisible}"
app:tint="?attr/color_main2_500"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
@@ -79,32 +66,32 @@
+ app:layout_constraintStart_toStartOf="@id/sip_identity"/>
@@ -115,9 +102,9 @@
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
- android:text="@{@string/password + `*`}"
+ android:text="@{@string/password + `*`, default=@string/password}"
app:layout_constraintWidth_max="@dimen/text_input_max_width"
- app:layout_constraintTop_toBottomOf="@id/username"
+ app:layout_constraintTop_toBottomOf="@id/sip_identity"
app:layout_constraintStart_toStartOf="@id/password"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/navigation/assistant_nav_graph.xml b/app/src/main/res/navigation/assistant_nav_graph.xml
index 20157b639..f217e088b 100644
--- a/app/src/main/res/navigation/assistant_nav_graph.xml
+++ b/app/src/main/res/navigation/assistant_nav_graph.xml
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/assistant_nav_graph"
- app:startDestination="@id/loginFragment">
+ app:startDestination="@id/landingFragment">
-
-
-
-
-
+
@@ -137,14 +114,64 @@
android:name="org.linphone.ui.assistant.fragment.PermissionsFragment"
android:label="PermissionsFragment"
tools:layout="@layout/assistant_permissions_fragment">
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 570639f33..5d374ec43 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -38,6 +38,7 @@
Dummy subject
SIP address
+ username@domain
Display name
Domain
Username