Skip to content

Conversation

@brandonpage
Copy link
Contributor

In this PR:

  • Allow the dev menu to be opened before a user has authenticated.
  • Add Login Options screen to dev menu.
    • Toggle-able options are correctly applied to login.
  • Small cleanup dev menu UI.
  • Values can now be easily copied from the dev info screen.

The work to actually use a dynamically supplied boot config will be in the next PR.

Screenshot_20251107-125132 Screenshot_20251107-125141 Screenshot_20251107-125305 Screenshot_20251107-125325

Comment on lines 343 to 346
// If debug LoginOptions were changed reload the webview.
if (SalesforceSDKManager.getInstance().isDebugBuild && webView.url != viewModel.loginUrl.value) {
viewModel.reloadWebView()
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If anyone has a better was to implement this I am all for it. Unfortunately, calling reload from LoginOptionsActivity does not update the webview (because it is not currently rendered, I assume).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If login url is not changed, will that reload?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If WebServer Flow is used getting a new authorization url cause the url to change because code verifier will be new. If UserAgent flow is used we set the URL to about_blank before getting a "new" authorization url (which will be identical to the old one if nothing else changes) to force the reload.

It is just annoying I have to call it here also.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if web server flow is not being used then loginUrl won't change.
On iOS, I restart authentication when coming out of showing the login options: https://github.com/forcedotcom/SalesforceMobileSDK-iOS/pull/3950/files#diff-1334e2942ea2928b6db7bca76fdebd960b717bc0463c404244b998a86ee08121R494

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WebView does reload if User Agent Flow is used, that was an issue with 13.0 that we fixed.

Since we are just popping the LoginOptionsActivity off of the back stack I have no way of telling LoginActivity directly (like through intent data or something) that it needs to reload. I think the new loginDevMenuReload boolean I just added to SalesforceSDKManager is a decent enough solution though.

@github-actions
Copy link

github-actions bot commented Nov 7, 2025

1 Warning
⚠️ libs/SalesforceSDK/AndroidManifest.xml#L0 - The project references RTL attributes, but does not explicitly enable or disable RTL support with android:supportsRtl in the manifest

Generated by 🚫 Danger

Comment on lines +294 to +296
@Preview(showBackground = true)
@Composable
fun OptionsTogglePreview() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pro Tip: If you simply move your Composables outside of the activity where you are calling things like SalesforceSDKManager.getInstance() you can add previews without rendering issues.

}
}

val previewBootConfig = object : BootConfig() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe for the preview, we use a bogus remote access consumer key / redirect uri?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this bootconfig file sticking around?

Copy link
Contributor

@wmathurin wmathurin Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean: are we keeping a bootconfig file going forward, or are we going to keep checking in the bootconfig file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that file going to continue to exist for the foreseeable future? AFAIK, it is. So I just read the values from it for the preview instead of having them hardcoded or making up bogus ones.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good.

OutlinedTextField(
value = dynamicScopes.value ?: "",
onValueChange = { dynamicScopes.value = it },
label = { Text("Scopes (comma separated)") },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On iOS, I expect space delimited scopes.

@wmathurin
Copy link
Contributor

The iOS equivalent: forcedotcom/SalesforceMobileSDK-iOS#3950

Copy link
Contributor

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻

Comment on lines -203 to -214
@Test
fun clearWebViewCache_CallsWebViewClearCache_WithTrueParameter() {
// Arrange
val mockWebView = mockk<WebView>(relaxed = true)

// Act
viewModel.clearWebViewCache(mockWebView)

// Assert
verify { mockWebView.clearCache(true) }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After many hours cannot explain this, but mocking the webview here causes all tests in LoginActivityTest class to hang and never finish. Killing the while test run.

Unmocking at the end of the suite, or in the test itself with a finally did not fix the issue. Creating a real webview and using spyk did not fix the issue.

The test only covers the one line passthrough of clearCache so I don't think it is worth keeping.

@codecov
Copy link

codecov bot commented Nov 12, 2025

Codecov Report

❌ Patch coverage is 78.74396% with 88 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.26%. Comparing base (6caa3e7) to head (6d4dd9e).
⚠️ Report is 1 commits behind head on dev.

Files with missing lines Patch % Lines
...rc/com/salesforce/androidsdk/ui/DevInfoActivity.kt 59.55% 19 Missing and 17 partials ⚠️
...m/salesforce/androidsdk/ui/LoginOptionsActivity.kt 89.20% 3 Missing and 16 partials ⚠️
.../salesforce/androidsdk/app/SalesforceSDKManager.kt 58.13% 14 Missing and 4 partials ⚠️
...rce/androidsdk/developer/support/DevSupportInfo.kt 86.27% 9 Missing and 5 partials ⚠️
.../src/com/salesforce/androidsdk/ui/LoginActivity.kt 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##                dev    #2801      +/-   ##
============================================
+ Coverage     56.92%   61.26%   +4.34%     
- Complexity     2527     2747     +220     
============================================
  Files           211      214       +3     
  Lines         16919    16931      +12     
  Branches       2369     2453      +84     
============================================
+ Hits           9631    10373     +742     
+ Misses         6241     5394     -847     
- Partials       1047     1164     +117     
Components Coverage Δ
Analytics 47.92% <ø> (ø)
SalesforceSDK 53.49% <78.74%> (+9.26%) ⬆️
Hybrid 59.05% <ø> (ø)
SmartStore 78.20% <ø> (ø)
MobileSync 78.87% <ø> (-2.81%) ⬇️
React 52.36% <ø> (ø)
Files with missing lines Coverage Δ
...sdk/reactnative/app/SalesforceReactSDKManager.java 65.51% <ø> (ø)
...otifications/local/ShowDeveloperSupportNotifier.kt 67.85% <ø> (+9.52%) ⬆️
...orce/androidsdk/ui/components/PickerBottomSheet.kt 56.12% <ø> (+0.50%) ⬆️
...ndroidsdk/smartstore/app/SmartStoreSDKManager.java 59.72% <ø> (ø)
.../src/com/salesforce/androidsdk/ui/LoginActivity.kt 24.13% <75.00%> (-0.13%) ⬇️
...rce/androidsdk/developer/support/DevSupportInfo.kt 86.27% <86.27%> (ø)
.../salesforce/androidsdk/app/SalesforceSDKManager.kt 58.13% <58.13%> (+5.73%) ⬆️
...m/salesforce/androidsdk/ui/LoginOptionsActivity.kt 89.20% <89.20%> (ø)
...rc/com/salesforce/androidsdk/ui/DevInfoActivity.kt 60.37% <59.55%> (+60.37%) ⬆️

... and 61 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@wmathurin
Copy link
Contributor

The iOS equivalent: forcedotcom/SalesforceMobileSDK-iOS#3950

On iOS, I also ended up adding more details in the dev infos screen (and changed it to have collapsible sections). See forcedotcom/SalesforceMobileSDK-iOS#3950 (comment)

@brandonpage
Copy link
Contributor Author

The iOS equivalent: forcedotcom/SalesforceMobileSDK-iOS#3950

On iOS, I also ended up adding more details in the dev infos screen (and changed it to have collapsible sections). See forcedotcom/SalesforceMobileSDK-iOS#3950 (comment)

Love it! I was thinking about categories a lot like that but didn't get around to implementing it. I'll add it when I get a chance.

…on change. Add test for LoginOptionsActivity.
@github-actions
Copy link

1 Warning
⚠️ Big PR, try to keep changes smaller if you can.

Generated by 🚫 Danger

@brandonpage
Copy link
Contributor Author

Last thing to do for this PR is to match the new DevMenu sections from the iOS PR. I have tests written for DevMenuActivity but did not commit them as they are about to change with my next commit.

@brandonpage
Copy link
Contributor Author

New Dev Info Screen:
newDevMenu

import java.text.SimpleDateFormat
import java.util.Locale

data class DevSupportInfo(
Copy link
Contributor

@wmathurin wmathurin Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On iOS I kept the Dev Info "dumb" to be able to support arbitrary grouping of rows - including rows provided by the app or subclasses of SalesforceSDKManager e.g. SmartStoreSDKManager here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I wanted to keep as much of the sorting logic out of the UI as I could... but I forgot about the subclasses. It would be easy to add the subclasses data as additional categories, but if you think I should revert back to the old simple list I can.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the dev infos provided by subclasses of SalesforceSDKManager are no longer shown (e.g. https://github.com/forcedotcom/SalesforceMobileSDK-Android/blob/dev/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/app/SmartStoreSDKManager.java#L470) .
At least we should fix that but we would still be breaking apps that implemented their own SDKManager and overrode getDevSupportInfos.

That's why I ended up going with the list that has "section:xxx" markers. It's a bit low tech ;-) but it keeps everything working.

Maybe the move to a typed dev infos could be done in 14.0??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DevSupportInfo

var smartStoreValues: List<Pair<String, String>>? = null

SmartStoreSDKManger

@Override
    public @NotNull DevSupportInfo getDevSupportInfo() {
        DevSupportInfo devInfo = super.getDevSupportInfo();
        devInfo.setSmartStoreValues(Arrays.asList(
                new Pair<>("SQLCipher version", getSmartStore().getSQLCipherVersion()),
                new Pair<>("SQLCipher Compile Options", TextUtils.join(", ", getSmartStore().getCompileOptions())),
                new Pair<>("SQLCipher Runtime Setting", TextUtils.join(", ", getSmartStore().getRuntimeSettings())),
                new Pair<>("User SmartStores", TextUtils.join(", ", getUserStoresPrefixList())),
                new Pair<>("Global SmartStores", TextUtils.join(", ", getGlobalStoresPrefixList())),
                new Pair<>("User Key-Value Stores", TextUtils.join(", ", getKeyValueStoresPrefixList())),
                new Pair<>("Global Key-Value Stores", TextUtils.join(", ", getGlobalKeyValueStoresPrefixList()))
        ));
        return devInfo;
    }

I was thinking this would be okay? The idea of grouping values based on a string that could change feels fragile but I will take a closer look at the iOS implementation before committing anything.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. DevSupportInfo should not know about SmartStore.
  2. That would work for SmartStoreSDKManager but would still break apps (if they exist) that override getDevSupportInfos().

Maybe DevSupportInfo could have a field for other infos which would be shown in the UI after everything else.
The DevSupportInfo that you create in SalesforceSDKManager could populate that new field by calling the existing getDevSupportInfos().
The getDevSupportInfos() method in SalesforceSDKManager would return an empty list.
SmartStoreSDKManager and other managers could be left unchanged and their additional infos would keep showing up in the UI.

If we want some grouping in the additional infos, we could do the "section:xxx" hack I did on iOS or do something more structured (but that would mean changing the subclasses to take advantage of it).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. DevSupportInfo should not know about SmartStore.

I think if SmartStore data is optional it isn't a problem. The UI has to check if SmartStore data exists one way or another, this is just more structured.

  1. That would work for SmartStoreSDKManager but would still break apps (if they exist) that override getDevSupportInfos().

Good point. I am going to try to massage the list from the existing API (even if it has been modified by a subclass) into the DevSupportInfo class. If I make everything nullable and allow additional sections to be added it should work well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wmathurin I have the approach in my latest commit. The DevSupportInfo class now allows for everything to be null on the main constructor, has a secondary constructor similar to the original constructor and features a createFromLegacyDevInfos function to instantiate the class from List<String>. A unit test insures that given the same input data the objects created by the secondary constructor and the createFromLegacyDevInfos function are identical.

So whatever is in the devSupportInfos (list of string) will be parsed to the expected categories. Removed values simply will not show up, completely empty sections will now show up and any additional info will be added to the first basic non-collapsable section. SmartStoreSDKManager's override of devSupportInfos works as expected, creating a new "Smart Store" section.

Code to be used post 14.0 is commented out in place.

@github-actions
Copy link

Job Summary for Gradle

Pull Request :: test-android
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
SalesforceMobileSDK-Android libs:SalesforceReact:assembleAndroidTest 8.14.2 Build Scan not published
SalesforceMobileSDK-Android libs:SalesforceReact:convertCodeCoverage 8.14.2 Build Scan not published

@github-actions
Copy link

Job Summary for Gradle

Pull Request :: test-android
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
SalesforceMobileSDK-Android libs:SalesforceReact:assembleAndroidTest 8.14.2 Build Scan not published
SalesforceMobileSDK-Android libs:SalesforceReact:convertCodeCoverage 8.14.2 Build Scan not published

…oid into login_options

# Conflicts:
#	.github/workflows/reusable-workflow.yaml
@github-actions
Copy link

Job Summary for Gradle

Pull Request :: test-android
Gradle Root Project Requested Tasks Gradle Version Build Outcome Build Scan®
SalesforceMobileSDK-Android libs:SalesforceReact:lint 8.14.3 Build Scan not published
SalesforceMobileSDK-Android libs:SalesforceReact:assembleAndroidTest 8.14.3 Build Scan not published
SalesforceMobileSDK-Android libs:SalesforceReact:convertCodeCoverage 8.14.3 Build Scan not published

@brandonpage brandonpage merged commit d5cfcdf into forcedotcom:dev Nov 18, 2025
22 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants