Skip to content

Commit 608b88e

Browse files
committed
Merge branch 'dev' of github.com:wmathurin/SalesforceMobileSDK-Android into feature/migrate-doc-generation-to-dokka
2 parents 9fb310e + 11a4a43 commit 608b88e

30 files changed

Lines changed: 1289 additions & 180 deletions

File tree

.claude/skills/update-sqlcipher/SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ When creating the PR:
178178

179179
Recent SQLCipher updates in the project:
180180

181-
- **4.10.0** - Current version (PR #2744)
181+
- **4.16.0** - SQLite 3.53.1, LibTomCrypt 1.18.2 (2026-05-12)
182+
- **4.15.0** - Previous version
183+
- **4.10.0** - Previous version (PR #2744)
182184
- **4.9.0** - Previous version (PR #2717)
183185
- **4.7.2** - Introduced `onCorruption()` API change (PR #2698)
184186
- **4.6.1** - Stable version (PR #2605)

external/cordova/cordova.js

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
under the License.
2020
*/
2121
;(function() {
22-
var PLATFORM_VERSION_BUILD_LABEL = '14.0.1';
22+
var PLATFORM_VERSION_BUILD_LABEL = '15.0.0';
2323
// file: src/scripts/require.js
2424
var require;
2525
var define;
@@ -986,7 +986,7 @@ function androidExec (success, fail, service, action, args) {
986986
var callbackId = service + cordova.callbackId++;
987987
var argsJson = JSON.stringify(args);
988988
if (success || fail) {
989-
cordova.callbacks[callbackId] = { success: success, fail: fail };
989+
cordova.callbacks[callbackId] = { success, fail };
990990
}
991991

992992
var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
@@ -1026,6 +1026,8 @@ function pollOnce (opt_fromOnlineEvent) {
10261026
}
10271027
}
10281028

1029+
androidExec.pollOnce = pollOnce;
1030+
10291031
function pollingTimerFunc () {
10301032
if (pollEnabled) {
10311033
pollOnce();
@@ -1429,6 +1431,10 @@ module.exports = {
14291431
// Core Splash Screen
14301432
modulemapper.clobbers('cordova/plugin/android/splashscreen', 'navigator.splashscreen');
14311433

1434+
// Attach the internal statusBar utility to window.statusbar
1435+
// see the file under plugin/android/statusbar.js
1436+
modulemapper.clobbers('cordova/plugin/android/statusbar', 'window.statusbar');
1437+
14321438
var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
14331439

14341440
// Inject a listener for the backbutton on the document.
@@ -1627,6 +1633,85 @@ module.exports = splashscreen;
16271633

16281634
});
16291635

1636+
// file: ../../cordova-js-src/plugin/android/statusbar.js
1637+
define("cordova/plugin/android/statusbar", function(require, exports, module) {
1638+
1639+
var exec = require('cordova/exec');
1640+
1641+
var statusBarVisible = true;
1642+
var statusBar = {};
1643+
1644+
// This <script> element is explicitly used by Cordova's statusbar for computing color. (Do not use this element)
1645+
const statusBarScript = document.createElement('script');
1646+
document.head.appendChild(statusBarScript);
1647+
1648+
Object.defineProperty(statusBar, 'visible', {
1649+
configurable: false,
1650+
enumerable: true,
1651+
get: function () {
1652+
if (window.StatusBar) {
1653+
// try to let the StatusBar plugin handle it
1654+
return window.StatusBar.isVisible;
1655+
}
1656+
1657+
return statusBarVisible;
1658+
},
1659+
set: function (value) {
1660+
if (window.StatusBar) {
1661+
// try to let the StatusBar plugin handle it
1662+
if (value) {
1663+
window.StatusBar.show();
1664+
} else {
1665+
window.StatusBar.hide();
1666+
}
1667+
} else {
1668+
statusBarVisible = value;
1669+
exec(null, null, 'SystemBarPlugin', 'setStatusBarVisible', [!!value]);
1670+
}
1671+
}
1672+
});
1673+
1674+
Object.defineProperty(statusBar, 'setBackgroundColor', {
1675+
configurable: false,
1676+
enumerable: false,
1677+
writable: false,
1678+
value: function (value) {
1679+
statusBarScript.style.color = value;
1680+
var rgbStr = window.getComputedStyle(statusBarScript).getPropertyValue('color');
1681+
1682+
if (!rgbStr.match(/^rgb/)) { return; }
1683+
1684+
var rgbVals = rgbStr.match(/\d+/g).map(function (v) { return parseInt(v, 10); });
1685+
1686+
if (rgbVals.length < 3) {
1687+
return;
1688+
} else if (rgbVals.length === 3) {
1689+
rgbVals = [255].concat(rgbVals);
1690+
}
1691+
1692+
// TODO: Use `padStart(2, '0')` once SDK 24 is dropped.
1693+
const padRgb = (val) => {
1694+
const hex = val.toString(16);
1695+
return hex.length === 1 ? '0' + hex : hex;
1696+
};
1697+
const a = padRgb(rgbVals[0]);
1698+
const r = padRgb(rgbVals[1]);
1699+
const g = padRgb(rgbVals[2]);
1700+
const b = padRgb(rgbVals[3]);
1701+
const hexStr = '#' + a + r + g + b;
1702+
1703+
if (window.StatusBar) {
1704+
window.StatusBar.backgroundColorByHexString(hexStr);
1705+
} else {
1706+
exec(null, null, 'SystemBarPlugin', 'setStatusBarBackgroundColor', rgbVals);
1707+
}
1708+
}
1709+
});
1710+
1711+
module.exports = statusBar;
1712+
1713+
});
1714+
16301715
// file: src/common/pluginloader.js
16311716
define("cordova/pluginloader", function(require, exports, module) {
16321717

libs/MobileSync/src/com/salesforce/androidsdk/mobilesync/app/MobileSyncSDKManager.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ public class MobileSyncSDKManager extends SmartStoreSDKManager {
5252

5353
private static final String TAG = "MobileSyncSDKManager";
5454

55+
/**
56+
* Protected constructor.
57+
*
58+
* @param context Application context.
59+
* @param mainActivity Activity that should be launched after the login flow.
60+
* @param loginActivity Login activity.
61+
* @param nativeLoginActivity Native login activity.
62+
* @param googleCloudProjectId Google Cloud project ID for app attestation (nullable).
63+
*/
64+
protected MobileSyncSDKManager(Context context, Class<? extends Activity> mainActivity,
65+
Class<? extends Activity> loginActivity,
66+
Class<? extends Activity> nativeLoginActivity,
67+
Long googleCloudProjectId) {
68+
super(context, mainActivity, loginActivity, nativeLoginActivity, googleCloudProjectId);
69+
}
70+
5571
/**
5672
* Protected constructor.
5773
*
@@ -63,7 +79,7 @@ public class MobileSyncSDKManager extends SmartStoreSDKManager {
6379
protected MobileSyncSDKManager(Context context, Class<? extends Activity> mainActivity,
6480
Class<? extends Activity> loginActivity,
6581
Class<? extends Activity> nativeLoginActivity) {
66-
super(context, mainActivity, loginActivity, nativeLoginActivity);
82+
this(context, mainActivity, loginActivity, nativeLoginActivity, null);
6783
}
6884

6985
private static void init(Context context, Class<? extends Activity> mainActivity,

libs/SalesforceHybrid/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ plugins {
1414

1515
dependencies {
1616
api(project(":libs:MobileSync"))
17-
api("org.apache.cordova:framework:14.0.1") // TODO: This update should happen in a dedicated work effort. ECJ20260423
17+
api("org.apache.cordova:framework:15.0.0")
1818
api("androidx.appcompat:appcompat:1.7.1")
1919
api("androidx.appcompat:appcompat-resources:1.7.1")
20-
api("androidx.webkit:webkit:1.15.0")
20+
api("androidx.webkit:webkit:1.16.0")
2121
api("androidx.core:core-splashscreen:1.2.0")
2222
implementation("androidx.core:core-ktx:1.18.0")
2323
androidTestImplementation("androidx.test:runner:1.7.0")

libs/SalesforceSDK/src/com/salesforce/androidsdk/accounts/UserAccount.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
*/
2727
package com.salesforce.androidsdk.accounts;
2828

29+
import android.accounts.Account;
30+
import android.accounts.AccountManager;
2931
import android.app.DownloadManager;
3032
import android.content.Context;
3133
import android.content.pm.PackageManager;
@@ -372,6 +374,46 @@ public String getAuthToken() {
372374
* @return Refresh token.
373375
*/
374376
public String getRefreshToken() {
377+
if (accountName != null) {
378+
try {
379+
final AccountManager accMrg = AccountManager.get(SalesforceSDKManager.getInstance().getAppContext());
380+
final String accountType = SalesforceSDKManager.getInstance().getAccountType();
381+
for (Account account : accMrg.getAccountsByType(accountType)) {
382+
if (accountName.equals(account.name)) {
383+
final String encryptedPassword = accMrg.getPassword(account);
384+
if (encryptedPassword != null) {
385+
final String newestRefreshToken = SalesforceSDKManager.decrypt(
386+
encryptedPassword, SalesforceSDKManager.getEncryptionKey());
387+
if (newestRefreshToken != null) {
388+
return newestRefreshToken;
389+
}
390+
}
391+
break;
392+
}
393+
}
394+
} catch (Exception e) {
395+
SalesforceSDKLogger.w(TAG,
396+
"Failed to read latest refresh token from AccountManager; using in-memory value",
397+
e);
398+
}
399+
}
400+
return refreshToken;
401+
}
402+
403+
/**
404+
* Returns the in-memory refresh token snapshot, without consulting
405+
* {@link AccountManager}.
406+
*
407+
* Intended for persistence call sites that are about to write a refresh
408+
* token to storage (account creation, update, token migration, duplicate
409+
* user reconciliation). Using {@link #getRefreshToken()} at those sites
410+
* would read back the value currently persisted in {@link AccountManager}
411+
* — i.e. the value we are about to overwrite — instead of the value the
412+
* caller built this {@code UserAccount} with.
413+
*
414+
* @return The refresh token field as set at construction time.
415+
*/
416+
public String getRefreshTokenForPersistence() {
375417
return refreshToken;
376418
}
377419

@@ -948,7 +990,7 @@ JSONObject toJson(List<String> additionalOauthKeys) {
948990
JSONObject object = new JSONObject();
949991
try {
950992
object.put(AUTH_TOKEN, authToken);
951-
object.put(REFRESH_TOKEN, refreshToken);
993+
object.put(REFRESH_TOKEN, getRefreshToken());
952994
object.put(LOGIN_SERVER, loginServer);
953995
object.put(ID_URL, idUrl);
954996
object.put(INSTANCE_SERVER, instanceServer);
@@ -1007,7 +1049,7 @@ public JSONObject toJson() {
10071049
Bundle toBundle(List<String> additionalOauthKeys) {
10081050
Bundle object = new Bundle();
10091051
object.putString(AUTH_TOKEN, authToken);
1010-
object.putString(REFRESH_TOKEN, refreshToken);
1052+
object.putString(REFRESH_TOKEN, getRefreshToken());
10111053
object.putString(LOGIN_SERVER, loginServer);
10121054
object.putString(ID_URL, idUrl);
10131055
object.putString(INSTANCE_SERVER, instanceServer);

libs/SalesforceSDK/src/com/salesforce/androidsdk/accounts/UserAccountManager.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ public Bundle createAccount(UserAccount userAccount) {
442442
final Bundle extras = buildAuthBundle(userAccount);
443443

444444
Account acc = new Account(userAccount.getAccountName(), accountType);
445-
String password = SalesforceSDKManager.encrypt(userAccount.getRefreshToken(), encryptionKey);
445+
String password = SalesforceSDKManager.encrypt(userAccount.getRefreshTokenForPersistence(), encryptionKey);
446446
boolean success = accountManager.addAccountExplicitly(acc, password, new Bundle());
447447

448448
// Add account will fail if the account already exists, so update refresh token.
@@ -488,6 +488,16 @@ public Bundle updateAccount(Account account, UserAccount userAccount) {
488488
accountManager.setUserData(account, key, extras.getString(key));
489489
}
490490

491+
// The refresh token is stored as the Account's password (see createAccount), not as user data,
492+
// so buildAuthBundle does not include it. Persist it explicitly here so that server-side
493+
// Use the in-memory snapshot rather than getRefreshToken(), which now performs a live lookup
494+
// against AccountManager and would return the updated value we may be about to write.
495+
final String refreshToken = userAccount.getRefreshTokenForPersistence();
496+
if (refreshToken != null) {
497+
final String encryptionKey = SalesforceSDKManager.getEncryptionKey();
498+
accountManager.setPassword(account, SalesforceSDKManager.encrypt(refreshToken, encryptionKey));
499+
}
500+
491501
return extras;
492502
}
493503

0 commit comments

Comments
 (0)