Skip to content

Commit 2b196ba

Browse files
committed
2 parents cce98dc + c672dd5 commit 2b196ba

54 files changed

Lines changed: 5350 additions & 601 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/test-shards/SalesforceSDK.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"shards": [
44
{
55
"name": "network",
6-
"comment": "REST client tests that make live server calls - isolated",
6+
"comment": "REST client tests that make live server calls",
77
"targets": [
88
"class com.salesforce.androidsdk.rest.RestClientTest",
99
"class com.salesforce.androidsdk.rest.ClientManagerTest",
@@ -14,7 +14,7 @@
1414
},
1515
{
1616
"name": "ui",
17-
"comment": "UI tests and security tests that may require user interaction",
17+
"comment": "Tests that exercise Activities/UI",
1818
"targets": [
1919
"class com.salesforce.androidsdk.ui.LoginViewActivityTest",
2020
"class com.salesforce.androidsdk.ui.PickerBottomSheetTest",
@@ -23,7 +23,9 @@
2323
"class com.salesforce.androidsdk.ui.PickerBottomSheetActivityTest",
2424
"class com.salesforce.androidsdk.ui.DevInfoActivityTest",
2525
"class com.salesforce.androidsdk.security.ScreenLockManagerTest",
26-
"class com.salesforce.androidsdk.security.BiometricAuthenticationManagerTest"
26+
"class com.salesforce.androidsdk.security.BiometricAuthenticationManagerTest",
27+
"class com.salesforce.androidsdk.ui.TokenMigrationActivityTest",
28+
"class com.salesforce.androidsdk.ui.TokenMigrationWebViewTest"
2729
]
2830
},
2931
{
@@ -68,7 +70,9 @@
6870
"notClass com.salesforce.androidsdk.rest.NotificationsActionsResponseBodyTest",
6971
"notClass com.salesforce.androidsdk.rest.files.RenditionTypeTest",
7072
"notClass com.salesforce.androidsdk.rest.files.ConnectUriBuilderTest",
71-
"notClass com.salesforce.androidsdk.rest.files.FileRequestsTest"
73+
"notClass com.salesforce.androidsdk.rest.files.FileRequestsTest",
74+
"notClass com.salesforce.androidsdk.ui.TokenMigrationActivityTest",
75+
"notClass com.salesforce.androidsdk.ui.TokenMigrationWebViewTest"
7276
]
7377
}
7478
]

.github/workflows/reusable-lib-workflow.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ jobs:
184184
fi
185185
done
186186
- name: Test Report
187-
uses: mikepenz/action-junit-report@v5
187+
uses: mikepenz/action-junit-report
188188
if: success() || failure()
189189
with:
190190
check_name: ${{ inputs.lib }} Test Results

.github/workflows/reusable-ui-workflow.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ jobs:
127127
fi
128128
done
129129
- name: Test Report
130-
uses: mikepenz/action-junit-report@v5
130+
uses: mikepenz/action-junit-report
131131
if: success() || failure()
132132
with:
133133
check_name: ${{ inputs.lib }} Test Results

libs/SalesforceSDK/AndroidManifest.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
1111
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
1212

13-
<application>
13+
<application android:supportsRtl="true">
1414

1515
<!-- Metadata for supported app restrictions -->
1616
<meta-data android:name="android.content.APP_RESTRICTIONS"
@@ -45,6 +45,12 @@
4545
android:configChanges="orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
4646
android:exported="true" />
4747

48+
<!-- Token Migration Activity -->
49+
<activity android:name="com.salesforce.androidsdk.ui.TokenMigrationActivity"
50+
android:excludeFromRecents="true"
51+
android:theme="@style/AccountSwitcher"
52+
android:exported="false" />
53+
4854
<!-- Screen Lock Activity-->
4955
<activity android:name="com.salesforce.androidsdk.ui.ScreenLockActivity"
5056
android:exported="false"

libs/SalesforceSDK/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88
`publish-module`
99
jacoco
1010
kotlin("plugin.serialization") version "2.0.21"
11+
kotlin("plugin.parcelize")
1112
}
1213

1314
dependencies {
@@ -22,6 +23,7 @@ dependencies {
2223
api("androidx.browser:browser:1.8.0") // Update requires API 36 compileSdk
2324
api("androidx.work:work-runtime-ktx:2.10.3")
2425

26+
implementation("com.google.accompanist:accompanist-drawablepainter:0.37.3")
2527
implementation("com.google.android.material:material:1.13.0") // remove this when all xml is gone
2628
implementation("androidx.appcompat:appcompat:1.7.1")
2729
implementation("androidx.biometric:biometric:1.2.0-alpha05")
@@ -48,6 +50,7 @@ dependencies {
4850

4951
androidTestImplementation("androidx.test:runner:1.7.0")
5052
androidTestImplementation("androidx.test:rules:1.7.0")
53+
androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
5154
androidTestImplementation("androidx.test.ext:junit:1.3.0")
5255
androidTestImplementation("androidx.arch.core:core-testing:2.2.0")
5356
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$composeVersion")

libs/SalesforceSDK/res/layout/sf__screen_lock.xml

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22

33
<resources>
4-
<!-- Dimensions used in sf__screen_lock.xml -->
5-
<dimen name="sf__screenlock_error_margin_top">10dp</dimen>
6-
<dimen name="sf__screenlock_error_margin_left">20dp</dimen>
7-
<dimen name="sf__screenlock_error_margin_right">20dp</dimen>
8-
94
<!-- Biometric Enrollment Prompt -->
105
<dimen name="sf__dialog_layout_padding">0dp</dimen>
11-
</resources>
6+
</resources>

libs/SalesforceSDK/res/values/sf__styles.xml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,16 +206,6 @@
206206
<item name="android:windowLightStatusBar">false</item>
207207
</style>
208208

209-
<style name="SalesforceSDK.ScreenLock.ErrorMessage">
210-
<item name="android:layout_height">wrap_content</item>
211-
<item name="android:gravity">center</item>
212-
<item name="android:layout_gravity">center_horizontal</item>
213-
<item name="android:layout_width">wrap_content</item>
214-
<item name="android:layout_marginTop">@dimen/sf__screenlock_error_margin_top</item>
215-
<item name="android:layout_marginLeft">@dimen/sf__screenlock_error_margin_left</item>
216-
<item name="android:layout_marginRight">@dimen/sf__screenlock_error_margin_right</item>
217-
</style>
218-
219209
<style name="SalesforceSDK_Fullscreen" parent="@style/Theme.AppCompat.DayNight.NoActionBar">
220210
<item name="android:windowNoTitle">true</item>
221211
<item name="android:windowActionBar">false</item>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright (c) 2026-present, salesforce.com, inc.
3+
* All rights reserved.
4+
* Redistribution and use of this software in source and binary forms, with or
5+
* without modification, are permitted provided that the following conditions
6+
* are met:
7+
* - Redistributions of source code must retain the above copyright notice, this
8+
* list of conditions and the following disclaimer.
9+
* - Redistributions in binary form must reproduce the above copyright notice,
10+
* this list of conditions and the following disclaimer in the documentation
11+
* and/or other materials provided with the distribution.
12+
* - Neither the name of salesforce.com, inc. nor the names of its contributors
13+
* may be used to endorse or promote products derived from this software without
14+
* specific prior written permission of salesforce.com, inc.
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25+
* POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
package com.salesforce.androidsdk.accounts
28+
29+
import android.content.Intent
30+
import com.salesforce.androidsdk.accounts.UserAccountManager.getInstance
31+
import com.salesforce.androidsdk.app.SalesforceSDKManager
32+
import com.salesforce.androidsdk.config.OAuthConfig
33+
import com.salesforce.androidsdk.ui.TokenMigrationActivity
34+
import com.salesforce.androidsdk.util.SalesforceSDKLogger
35+
import java.util.UUID
36+
37+
const val TAG = "UserAccountManager"
38+
39+
/**
40+
* Attempts to migrate the [userAccount] to the provided Connected App or
41+
* External Client Application [appConfig].
42+
*
43+
* This might cause the approve/deny screen to be presented to the user to authorize the
44+
* new app. If successful a new set of credentials (refresh token, access token) are obtained
45+
* and replace the existing credentials for the user.
46+
*/
47+
@Suppress("UnusedReceiverParameter")
48+
fun UserAccountManager.migrateRefreshToken(
49+
userAccount: UserAccount? = getInstance().currentUser,
50+
appConfig: OAuthConfig,
51+
onMigrationSuccess: (userAccount: UserAccount) -> Unit,
52+
onMigrationError: (error: String, errorDesc: String?, e: Throwable?) -> Unit,
53+
) {
54+
val loggedOnSuccess: (userAccount: UserAccount) -> Unit = { user ->
55+
SalesforceSDKLogger.i(TAG, "Token Migration Successful \n\nUser ${user.username} " +
56+
"(${user.instanceServer}) successfully migrated to: \n$appConfig.")
57+
onMigrationSuccess.invoke(user)
58+
}
59+
val userId = userAccount?.userId
60+
val orgId = userAccount?.orgId
61+
62+
if (userId == null || orgId == null) {
63+
val message = "User account, userId or orgId is null."
64+
SalesforceSDKLogger.e(TAG, message)
65+
onMigrationError(message, null, null)
66+
return
67+
}
68+
69+
val callbackKey = MigrationCallbackRegistry.register(
70+
callbacks = MigrationCallbackRegistry.MigrationCallbacks(
71+
onMigrationSuccess = loggedOnSuccess,
72+
onMigrationError = onMigrationError,
73+
)
74+
)
75+
76+
with(SalesforceSDKManager.getInstance().appContext) {
77+
startActivity(
78+
Intent(/* packageContext = */ this, TokenMigrationActivity::class.java).apply {
79+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
80+
putExtra(TokenMigrationActivity.EXTRA_ORG_ID, orgId)
81+
putExtra(TokenMigrationActivity.EXTRA_USER_ID, userId)
82+
putExtra(TokenMigrationActivity.EXTRA_OAUTH_CONFIG, appConfig)
83+
putExtra(TokenMigrationActivity.EXTRA_CALLBACK_ID, callbackKey)
84+
}
85+
)
86+
}
87+
}
88+
89+
/*
90+
This mechanism is used to pass a _string_ id to the Activity to retrieve callback functions.
91+
92+
Lambda functions may appear Parcelable/Serializable but since we cannot guarantee the
93+
content are they should not be passed. For instance, if the lambda function contains
94+
compose state an exception will be thrown.
95+
*/
96+
internal object MigrationCallbackRegistry {
97+
private val callbacks = mutableMapOf<String, MigrationCallbacks>()
98+
99+
data class MigrationCallbacks(
100+
val onMigrationSuccess: (UserAccount) -> Unit,
101+
val onMigrationError: (String, String?, Throwable?) -> Unit
102+
)
103+
104+
fun register(callbacks: MigrationCallbacks): String {
105+
val key = UUID.randomUUID().toString()
106+
this.callbacks[key] = callbacks
107+
return key
108+
}
109+
110+
fun consume(key: String): MigrationCallbacks? = callbacks.remove(key)
111+
}

0 commit comments

Comments
 (0)