Skip to content

Commit f4e39da

Browse files
authored
Merge pull request #2750 from npars/W-19307934_include-user-account
@W-19307934 Add UserAccount to Logout Broadcast
2 parents c8902c2 + 8ca6bab commit f4e39da

4 files changed

Lines changed: 103 additions & 16 deletions

File tree

libs/SalesforceSDK/src/com/salesforce/androidsdk/app/SalesforceSDKManager.kt

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -734,11 +734,9 @@ open class SalesforceSDKManager protected constructor(
734734
*/
735735
private fun cleanUp(
736736
frontActivity: Activity?,
737-
account: Account?,
737+
userAccount: UserAccount?,
738738
shouldDismissActivity: Boolean
739739
) {
740-
val userAccount = UserAccountManager.getInstance().buildUserAccount(account)
741-
742740
// Clean up within this process
743741
cleanUp(userAccount)
744742

@@ -1080,14 +1078,15 @@ open class SalesforceSDKManager protected constructor(
10801078
frontActivity: Activity?,
10811079
logoutReason: LogoutReason,
10821080
) {
1081+
val userAccount = UserAccountManager.getInstance().buildUserAccount(account)
10831082
cleanUp(
10841083
frontActivity,
1085-
account,
1084+
userAccount,
10861085
showLoginPage
10871086
)
10881087
clientMgr.removeAccount(account)
10891088
isLoggingOut = false
1090-
notifyLogoutComplete(showLoginPage, logoutReason)
1089+
notifyLogoutComplete(showLoginPage, logoutReason, userAccount)
10911090

10921091
// Revoke the existing refresh token
10931092
if (shouldLogoutWhenTokenRevoked() && refreshToken != null) {
@@ -1110,9 +1109,9 @@ open class SalesforceSDKManager protected constructor(
11101109
* Sends the logout complete event.
11111110
* @param showLoginPage When true, shows the login page
11121111
*/
1113-
private fun notifyLogoutComplete(showLoginPage: Boolean, logoutReason: LogoutReason) {
1112+
private fun notifyLogoutComplete(showLoginPage: Boolean, logoutReason: LogoutReason, userAccount: UserAccount?) {
11141113
EventsObservable.get().notifyEvent(LogoutComplete, logoutReason)
1115-
sendLogoutCompleteIntent(logoutReason)
1114+
sendLogoutCompleteIntent(logoutReason, userAccount)
11161115
if (showLoginPage) {
11171116
startSwitcherActivityIfRequired()
11181117
}
@@ -1416,12 +1415,15 @@ open class SalesforceSDKManager protected constructor(
14161415
} ?: ""
14171416

14181417
/** Sends the logout completed intent */
1419-
private fun sendLogoutCompleteIntent(logoutReason: LogoutReason) =
1418+
private fun sendLogoutCompleteIntent(logoutReason: LogoutReason, userAccount: UserAccount?) =
14201419
appContext.sendBroadcast(Intent(
14211420
LOGOUT_COMPLETE_INTENT_ACTION
14221421
).apply {
14231422
setPackage(appContext.packageName)
14241423
putExtra(LOGOUT_REASON_KEY, logoutReason.toString())
1424+
userAccount?.let { userAccount ->
1425+
putExtra(USER_ACCOUNT_KEY, userAccount.toBundle())
1426+
}
14251427
})
14261428

14271429
/**
@@ -1439,7 +1441,7 @@ open class SalesforceSDKManager protected constructor(
14391441
)
14401442
userAccount?.let { userAccount ->
14411443
putExtra(
1442-
USER_ACCOUNT,
1444+
USER_ACCOUNT_KEY,
14431445
userAccount.toBundle()
14441446
)
14451447
}
@@ -1452,7 +1454,7 @@ open class SalesforceSDKManager protected constructor(
14521454
intent: Intent
14531455
) {
14541456
if (intent.action == CLEANUP_INTENT_ACTION && intent.getStringExtra(PROCESS_ID_KEY) != PROCESS_ID) {
1455-
cleanUp(intent.getBundleExtra(USER_ACCOUNT)?.let { bundle ->
1457+
cleanUp(intent.getBundleExtra(USER_ACCOUNT_KEY)?.let { bundle ->
14561458
UserAccount(bundle)
14571459
})
14581460
}
@@ -1629,7 +1631,7 @@ open class SalesforceSDKManager protected constructor(
16291631
private val PROCESS_ID = randomUUID().toString()
16301632

16311633
/** The user account key for broadcast intents */
1632-
private const val USER_ACCOUNT = "userAccount"
1634+
internal const val USER_ACCOUNT_KEY = "userAccount"
16331635

16341636
/** An intent action indicating logout was completed */
16351637
const val LOGOUT_COMPLETE_INTENT_ACTION = "com.salesforce.LOGOUT_COMPLETE"

libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/SalesforceActivityDelegate.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
import android.view.KeyEvent;
3434

3535
import androidx.annotation.NonNull;
36+
import androidx.annotation.Nullable;
3637
import androidx.core.content.ContextCompat;
3738

39+
import com.salesforce.androidsdk.accounts.UserAccount;
3840
import com.salesforce.androidsdk.accounts.UserAccountManager;
3941
import com.salesforce.androidsdk.app.SalesforceSDKManager;
4042
import com.salesforce.androidsdk.auth.OAuth2;
@@ -145,7 +147,10 @@ protected void onUserSwitch() {
145147
private class ActivityLogoutCompleteReceiver extends LogoutCompleteReceiver {
146148

147149
@Override
148-
protected void onLogoutComplete(@NonNull OAuth2.LogoutReason reason) {
150+
protected void onLogoutComplete(
151+
@NonNull OAuth2.LogoutReason reason,
152+
@Nullable UserAccount userAccount
153+
) {
149154
((SalesforceActivityInterface) activity).onLogoutComplete();
150155
}
151156
}

libs/SalesforceSDK/src/com/salesforce/androidsdk/util/LogoutCompleteReceiver.kt

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,45 @@ package com.salesforce.androidsdk.util
2929
import android.content.BroadcastReceiver
3030
import android.content.Context
3131
import android.content.Intent
32+
import com.salesforce.androidsdk.accounts.UserAccount
3233
import com.salesforce.androidsdk.app.SalesforceSDKManager
34+
import com.salesforce.androidsdk.app.SalesforceSDKManager.Companion.USER_ACCOUNT_KEY
3335
import com.salesforce.androidsdk.auth.OAuth2.LogoutReason
34-
import java.util.Locale
3536

3637
/**
3738
* Listens for the logout complete event, and acts on it.
3839
*/
3940
abstract class LogoutCompleteReceiver : BroadcastReceiver() {
4041
override fun onReceive(context: Context, intent: Intent) {
4142
if (intent.action == SalesforceSDKManager.LOGOUT_COMPLETE_INTENT_ACTION) {
42-
val reason = intent.getStringExtra(SalesforceSDKManager.LOGOUT_REASON_KEY) ?: LogoutReason.UNKNOWN.toString()
43-
onLogoutComplete(LogoutReason.valueOf(reason.uppercase(Locale.ROOT)))
43+
val reason = intent.getStringExtra(SalesforceSDKManager.LOGOUT_REASON_KEY)?.let {
44+
LogoutReason.valueOf(it.uppercase())
45+
} ?: LogoutReason.UNKNOWN
46+
val userAccount = intent.getBundleExtra(USER_ACCOUNT_KEY)?.let { bundle ->
47+
UserAccount(bundle)
48+
}
49+
@Suppress("DEPRECATION")
50+
onLogoutComplete(reason)
51+
onLogoutComplete(reason, userAccount)
4452
}
4553
}
4654

47-
protected abstract fun onLogoutComplete(reason: LogoutReason)
55+
/**
56+
* Called when logout is complete.
57+
*
58+
* @param reason The reason for the logout. If no reason is available, [LogoutReason.UNKNOWN] is used.
59+
*/
60+
@Deprecated(
61+
message = "To be removed in 14.0. Use onLogoutComplete method that includes the userAccount that was logged out.",
62+
replaceWith = ReplaceWith("onLogoutComplete(reason: LogoutReason, userAccount: UserAccount?)")
63+
)
64+
protected open fun onLogoutComplete(reason: LogoutReason) {}
65+
66+
/**
67+
* Called when logout is complete.
68+
*
69+
* @param reason The reason for the logout. If no reason is available, [LogoutReason.UNKNOWN] is used.
70+
* @param userAccount The user account that was logged out. If no user account is available, null is used.
71+
*/
72+
protected open fun onLogoutComplete(reason: LogoutReason, userAccount: UserAccount?) {}
4873
}

libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/accounts/UserAccountManagerTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,34 @@
2626
*/
2727
package com.salesforce.androidsdk.accounts;
2828

29+
import static androidx.core.content.ContextCompat.RECEIVER_NOT_EXPORTED;
30+
import static com.salesforce.androidsdk.accounts.UserAccountTest.TEST_USERNAME;
2931
import static com.salesforce.androidsdk.accounts.UserAccountTest.checkSameUserAccount;
3032

3133
import android.accounts.Account;
3234
import android.accounts.AccountManager;
3335
import android.content.Context;
36+
import android.content.IntentFilter;
3437

38+
import androidx.core.content.ContextCompat;
3539
import androidx.test.ext.junit.runners.AndroidJUnit4;
3640
import androidx.test.filters.SmallTest;
3741
import androidx.test.platform.app.InstrumentationRegistry;
3842

43+
import com.salesforce.androidsdk.app.SalesforceSDKManager;
3944
import com.salesforce.androidsdk.auth.OAuth2;
45+
import com.salesforce.androidsdk.util.LogoutCompleteReceiver;
4046

47+
import org.jetbrains.annotations.NotNull;
48+
import org.jetbrains.annotations.Nullable;
4149
import org.junit.After;
4250
import org.junit.Assert;
4351
import org.junit.Before;
4452
import org.junit.Test;
4553
import org.junit.runner.RunWith;
4654

4755
import java.util.List;
56+
import java.util.concurrent.Semaphore;
4857

4958
/**
5059
* Tests for UserAccountManager.
@@ -59,20 +68,27 @@ public class UserAccountManagerTest {
5968

6069
private UserAccountManager userAccMgr;
6170
private AccountManager accMgr;
71+
private FakeLogoutCompleteReceiver logoutCompleteReceiver;
6272

6373
@Before
6474
public void setUp() throws Exception {
6575
final Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
6676
accMgr = AccountManager.get(targetContext);
6777
userAccMgr = UserAccountManager.getInstance();
6878
Assert.assertNull("There should be no authenticated users", userAccMgr.getAuthenticatedUsers());
79+
logoutCompleteReceiver = new FakeLogoutCompleteReceiver();
80+
ContextCompat.registerReceiver(targetContext, logoutCompleteReceiver,
81+
new IntentFilter(SalesforceSDKManager.LOGOUT_COMPLETE_INTENT_ACTION), RECEIVER_NOT_EXPORTED);
6982
}
7083

7184
@After
7285
public void tearDown() throws Exception {
86+
final Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
87+
targetContext.unregisterReceiver(logoutCompleteReceiver);
7388
cleanupAccounts(accMgr);
7489
userAccMgr = null;
7590
accMgr = null;
91+
logoutCompleteReceiver = null;
7692
}
7793

7894
/**
@@ -165,6 +181,9 @@ public void testSignoutCurrentUser() {
165181
Assert.assertEquals("There should be 1 authenticated user", 1, userAccMgr.getAuthenticatedUsers().size());
166182
userAccMgr.signoutCurrentUser(null, true, OAuth2.LogoutReason.USER_LOGOUT);
167183
Assert.assertNull("There should be no authenticated users", userAccMgr.getAuthenticatedUsers());
184+
Assert.assertEquals(OAuth2.LogoutReason.USER_LOGOUT, logoutCompleteReceiver.getLastReasonReceived());
185+
Assert.assertNotNull(logoutCompleteReceiver.getLastUserAccountReceived());
186+
Assert.assertEquals(TEST_USERNAME, logoutCompleteReceiver.getLastUserAccountReceived().getUsername());
168187
}
169188

170189
/**
@@ -177,6 +196,9 @@ public void testSignoutBackgroundUser() {
177196
userAccMgr.signoutUser(firstUser, null, false, OAuth2.LogoutReason.USER_LOGOUT);
178197
Assert.assertEquals("There should be 1 authenticated user", 1, userAccMgr.getAuthenticatedUsers().size());
179198
checkSameUserAccount(secondUser, userAccMgr.getCurrentUser());
199+
Assert.assertEquals(OAuth2.LogoutReason.USER_LOGOUT, logoutCompleteReceiver.getLastReasonReceived());
200+
Assert.assertNotNull(logoutCompleteReceiver.getLastUserAccountReceived());
201+
Assert.assertEquals(TEST_USERNAME, logoutCompleteReceiver.getLastUserAccountReceived().getUsername());
180202
}
181203

182204
/**
@@ -210,4 +232,37 @@ private UserAccount createOtherTestAccountInAccountManager() {
210232
return userAccount;
211233
}
212234

235+
private static class FakeLogoutCompleteReceiver extends LogoutCompleteReceiver {
236+
private OAuth2.LogoutReason lastReasonReceived;
237+
private UserAccount lastUserAccountReceived;
238+
// Use a semaphore here to ensure the test doesn't proceed until the logout complete
239+
// receiver has been called
240+
private final Semaphore completionSemaphore = new Semaphore(0);
241+
242+
protected void onLogoutComplete(@NotNull OAuth2.LogoutReason reason, @Nullable UserAccount userAccount) {
243+
lastReasonReceived = reason;
244+
lastUserAccountReceived = userAccount;
245+
completionSemaphore.release();
246+
}
247+
248+
public OAuth2.LogoutReason getLastReasonReceived() {
249+
try {
250+
completionSemaphore.acquire();
251+
} catch (InterruptedException e) {
252+
Assert.fail("Interrupted while waiting for lastReasonReceived to be set");
253+
}
254+
completionSemaphore.release();
255+
return lastReasonReceived;
256+
}
257+
258+
public UserAccount getLastUserAccountReceived() {
259+
try {
260+
completionSemaphore.acquire();
261+
} catch (InterruptedException e) {
262+
Assert.fail("Interrupted while waiting for lastUserAccountReceived to be set");
263+
}
264+
completionSemaphore.release();
265+
return lastUserAccountReceived;
266+
}
267+
}
213268
}

0 commit comments

Comments
 (0)