@@ -147,9 +147,9 @@ import com.salesforce.androidsdk.util.SalesforceSDKLogger.e
147147import com.salesforce.androidsdk.util.SalesforceSDKLogger.w
148148import com.salesforce.androidsdk.util.UriFragmentParser
149149import kotlinx.coroutines.CoroutineScope
150- import kotlinx.coroutines.Dispatchers
151150import kotlinx.coroutines.Dispatchers.Default
152151import kotlinx.coroutines.Dispatchers.IO
152+ import kotlinx.coroutines.Dispatchers.Main
153153import kotlinx.coroutines.launch
154154import org.json.JSONObject
155155import java.lang.String.format
@@ -175,6 +175,8 @@ import java.security.cert.X509Certificate
175175 * them.
176176 */
177177open class LoginActivity : FragmentActivity () {
178+ private var customTabLauncher: ActivityResultLauncher <Intent >? = null
179+
178180 // View Model
179181 @VisibleForTesting(otherwise = PROTECTED )
180182 open val viewModel: LoginViewModel
@@ -226,7 +228,7 @@ open class LoginActivity : FragmentActivity() {
226228 SalesforceSDKManager .getInstance().setViewNavigationVisibility(this )
227229 }
228230
229- applyIntent(intent )
231+ applyIntent()
230232
231233 // Don't let sharedBrowserSession org setting stop a new user from logging in.
232234 if (intent.extras?.getBoolean(NEW_USER ) == true ) {
@@ -272,7 +274,7 @@ open class LoginActivity : FragmentActivity() {
272274 onBackPressedDispatcher.addCallback { handleBackBehavior() }
273275 }
274276
275- val customTabLauncher: ActivityResultLauncher < Intent > = registerForActivityResult(
277+ customTabLauncher = registerForActivityResult(
276278 ActivityResultContracts .StartActivityForResult ()
277279 ) { result: ActivityResult ->
278280 // Check if the user backed out of the custom tab.
@@ -287,48 +289,6 @@ open class LoginActivity : FragmentActivity() {
287289 }
288290 }
289291
290- // Take action on selected server change.
291- viewModel.selectedServer.observe(this ) { selectedServer ->
292-
293- // Guard against observing a selected server already provided by the intent data, such as a Salesforce Welcome Discovery mobile URL.
294- val selectedServerUri = selectedServer.toUri()
295- if (intent.data?.host == selectedServerUri.host) {
296- return @observe
297- }
298-
299- // Use the URL to switch between default or Salesforce Welcome Discovery log in, if applicable.
300- if (switchDefaultOrSalesforceWelcomeDiscoveryLogin(selectedServerUri)) {
301- return @observe
302- }
303-
304- if (viewModel.singleServerCustomTabActivity) {
305- // Skip fetching authorization and show custom tab immediately.
306- viewModel.reloadWebView()
307- viewModel.loginUrl.value?.let { url ->
308- loadLoginPageInCustomTab(url, customTabLauncher)
309- }
310- } else {
311- with (SalesforceSDKManager .getInstance()) {
312- // Fetch well known config and load in custom tab if required.
313- fetchAuthenticationConfiguration {
314- /* Browser-based authentication is applicable when not authenticating with a front-door bridge URL */
315- if (isBrowserLoginEnabled && ! viewModel.isUsingFrontDoorBridge) {
316- if (useWebServerAuthentication) {
317- viewModel.loginUrl.value?.let { url -> loadLoginPageInCustomTab(url, customTabLauncher) }
318- } else {
319- /* Reload the webview now that isBrowserLoginEnabled has been set
320- to true so that we generate an authorization URL with PKCE values. */
321- lifecycleScope.launch(Dispatchers .Main ) {
322- viewModel.reloadWebView()
323- viewModel.loginUrl.value?.let { url -> loadLoginPageInCustomTab(url, customTabLauncher) }
324- }
325- }
326- }
327- }
328- }
329- }
330- }
331-
332292 // Support magic links
333293 if (viewModel.jwt != null ) {
334294 swapJWTForAccessToken()
@@ -359,7 +319,9 @@ open class LoginActivity : FragmentActivity() {
359319 return
360320 }
361321
362- applyIntent(intent)
322+ // Store the new intent and apply it to the activity.
323+ setIntent(intent)
324+ applyIntent()
363325 }
364326
365327 private fun clearWebView (showServerPicker : Boolean = true) {
@@ -925,6 +887,9 @@ open class LoginActivity : FragmentActivity() {
925887 // endregion
926888 // region Salesforce Welcome Login Private Implementation
927889
890+ /* * The previously observed pending login server for use in switching between default and Salesforce Welcome Discovery log in */
891+ private var previousPendingLoginServer: String? = null
892+
928893 /* *
929894 * If the intent is for Salesforce Welcome Discovery, apply it to the activity.
930895 * @param intent The intent
@@ -985,51 +950,51 @@ open class LoginActivity : FragmentActivity() {
985950 .build()
986951
987952 /* *
988- * Determines if the provided proposed selected server URL is a switch from
953+ * Determines if the provided pending login server URL is a switch from
989954 * Salesforce Welcome Discovery back to default log in.
990- * @param proposedSelectedServerUrl The proposed selected server URL
991- * @return Boolean true if the provided proposed selected server URL is a
955+ * @param pendingLoginServerUri The pending login server URL
956+ * @return Boolean true if the provided pending login server URL is a
992957 * switch from Salesforce Welcome Discovery back to the default log in,
993958 * false otherwise.
994959 * */
995960 private fun isSwitchFromSalesforceWelcomeDiscoveryToDefaultLogin (
996- proposedSelectedServerUrl : Uri
997- ) = viewModel.loginUrl.value ?.toUri()?.let { loginUrl ->
998- isSalesforceWelcomeDiscoveryMobileUrl( this , loginUrl ) && ! (isSalesforceWelcomeDiscoveryMobileUrl(this , proposedSelectedServerUrl ))
961+ pendingLoginServerUri : Uri
962+ ) = previousPendingLoginServer ?.toUri()?.let { previousPendingLoginServerUri ->
963+ isSalesforceWelcomeDiscoveryUrlPath(previousPendingLoginServerUri ) && ! (isSalesforceWelcomeDiscoveryMobileUrl(this , pendingLoginServerUri ))
999964 } ? : false
1000965
1001966 /* *
1002967 * Switches between default or Salesforce Welcome Discovery log in as needed
1003- * using the provided proposed selected server URL.
1004- * @param uri The proposed selected server URL
968+ * using the provided pending login server URL.
969+ * @param pendingLoginServerUri The pending login server URL
1005970 * @return Boolean true if a switch between default or Salesforce Welcome
1006971 * Discovery log is made, false otherwise.
1007972 */
1008- private fun switchDefaultOrSalesforceWelcomeDiscoveryLogin (uri : Uri ) =
973+ private fun switchDefaultOrSalesforceWelcomeDiscoveryLogin (pendingLoginServerUri : Uri ) =
1009974
1010- // If the selected server has changed to a new Salesforce Welcome Discovery URL and host.
1011- if (isSalesforceWelcomeDiscoveryUrlPath(uri )) {
975+ // If the pending login server is a change to a new Salesforce Welcome Discovery URL and host.
976+ if (isSalesforceWelcomeDiscoveryUrlPath(pendingLoginServerUri )) {
1012977
1013978 // Navigate to Salesforce Welcome Discovery.
1014979 startActivity(
1015980 Intent (
1016981 this ,
1017- LoginActivity :: class .java
982+ SalesforceSDKManager .getInstance().loginActivityClass
1018983 ).apply {
1019- data = generateSalesforceWelcomeDiscoveryMobileUrl(uri )
984+ data = generateSalesforceWelcomeDiscoveryMobileUrl(pendingLoginServerUri )
1020985 flags = FLAG_ACTIVITY_SINGLE_TOP
1021986 })
1022987 true
1023988 }
1024989
1025- // If the new selected server isn't a Salesforce Welcome Discovery URL but the previous was...
1026- else if (isSwitchFromSalesforceWelcomeDiscoveryToDefaultLogin(uri )) {
990+ // If the pending login server isn't a Salesforce Welcome Discovery URL but the previous was...
991+ else if (isSwitchFromSalesforceWelcomeDiscoveryToDefaultLogin(pendingLoginServerUri )) {
1027992
1028- // Navigate to login.
993+ // Navigate to default login.
1029994 startActivity(
1030995 Intent (
1031996 this ,
1032- LoginActivity :: class .java
997+ SalesforceSDKManager .getInstance().loginActivityClass
1033998 ).apply {
1034999 flags = FLAG_ACTIVITY_SINGLE_TOP
10351000 })
@@ -1051,6 +1016,8 @@ open class LoginActivity : FragmentActivity() {
10511016 uri : Uri
10521017 ): Boolean {
10531018 return if (isSalesforceWelcomeDiscoveryMobileCallbackUrl(uri)) {
1019+ // Stop observing the pending login server since the newly started default login will do so.
1020+ viewModel.pendingServer.removeObservers(this )
10541021 startDefaultLoginWithHintAndHost(
10551022 context = this ,
10561023 loginHint = uri.getQueryParameter(SALESFORCE_WELCOME_DISCOVERY_MOBILE_CALLBACK_URL_QUERY_PARAMETER_KEY_LOGIN_HINT ) ? : return false ,
@@ -1075,17 +1042,81 @@ open class LoginActivity : FragmentActivity() {
10751042 // endregion
10761043
10771044 /* *
1078- * Applies a new intent to the activity, for instance when the activity is
1079- * created or receives a new intent.
1080- * @param intent The new intent
1045+ * (Re-)applies the intent to the activity, for instance when the activity
1046+ * is created or receives a new intent.
10811047 */
1082- private fun applyIntent (intent : Intent ) {
1048+ private fun applyIntent () {
10831049
10841050 // If the intent is for Salesforce Welcome Discovery, apply it to the activity.
10851051 applySalesforceWelcomeDiscoveryIntent(intent)
10861052
10871053 // If the intent is for log in using a UI Bridge API front door URL, apply it to the activity.
10881054 applyUiBridgeApiFrontDoorUrl(intent)
1055+
1056+ // Apply the pending login server, if applicable.
1057+ applyPendingServer()
1058+ }
1059+
1060+ /* *
1061+ * Applies the pending login server. The authentication configuration will
1062+ * be fetched for the pending server and any applicable tasks will be
1063+ * performed such as browser based authentication. The pending login
1064+ * server will be set as the selected login server once the authentication
1065+ * configuration has been fetched.
1066+ */
1067+ private fun applyPendingServer () {
1068+
1069+ // Reset the observer to consume the current and future values.
1070+ viewModel.pendingServer.removeObservers(this )
1071+ viewModel.pendingServer.observe(this ) { pendingServer ->
1072+
1073+ // Guard against observing a pending login server already provided by the intent data, such as a Salesforce Welcome Discovery mobile URL.
1074+ val pendingServerUri = pendingServer.toUri()
1075+ if (intent.data?.host == pendingServerUri.host) {
1076+ previousPendingLoginServer = pendingServer
1077+ return @observe
1078+ }
1079+
1080+ // Use the URL to switch between default or Salesforce Welcome Discovery log in, if applicable.
1081+ if (switchDefaultOrSalesforceWelcomeDiscoveryLogin(pendingServerUri)) {
1082+ previousPendingLoginServer = pendingServer
1083+ return @observe
1084+ }
1085+
1086+ // Recall this pending login server for reference by future updates.
1087+ previousPendingLoginServer = pendingServer
1088+
1089+ if (viewModel.singleServerCustomTabActivity) {
1090+ // Skip fetching authorization and show custom tab immediately.
1091+ viewModel.selectedServer.value = pendingServer
1092+ viewModel.reloadWebView()
1093+ viewModel.loginUrl.value?.let { url ->
1094+ customTabLauncher?.let { loadLoginPageInCustomTab(url, it) }
1095+ }
1096+ } else {
1097+ with (SalesforceSDKManager .getInstance()) {
1098+ // Fetch the authentication configuration and load the login page in a custom tab if required.
1099+ fetchAuthenticationConfiguration {
1100+ lifecycleScope.launch(Main ) {
1101+ viewModel.selectedServer.value = pendingServer
1102+ /* Browser-based authentication is applicable when not authenticating with a front-door bridge URL */
1103+ if (isBrowserLoginEnabled && ! viewModel.isUsingFrontDoorBridge) {
1104+ if (useWebServerAuthentication) {
1105+ viewModel.loginUrl.value?.let { url ->
1106+ customTabLauncher?.let { loadLoginPageInCustomTab(url, it) }
1107+ }
1108+ } else {
1109+ /* Reload the webview now that isBrowserLoginEnabled has been set
1110+ to true so that we generate an authorization URL with PKCE values. */
1111+ viewModel.reloadWebView()
1112+ viewModel.loginUrl.value?.let { url -> customTabLauncher?.let { loadLoginPageInCustomTab(url, it) } }
1113+ }
1114+ }
1115+ }
1116+ }
1117+ }
1118+ }
1119+ }
10891120 }
10901121
10911122 /* *
@@ -1511,7 +1542,7 @@ open class LoginActivity : FragmentActivity() {
15111542 loginHint : String ,
15121543 loginHost : String ,
15131544 ) {
1514- Intent (context, LoginActivity :: class .java ).apply {
1545+ Intent (context, SalesforceSDKManager .getInstance().loginActivityClass ).apply {
15151546 putExtra(EXTRA_KEY_LOGIN_HINT , loginHint)
15161547 putExtra(EXTRA_KEY_LOGIN_HOST , loginHost)
15171548 flags = FLAG_ACTIVITY_SINGLE_TOP
0 commit comments