Feat/lms directory#483
Conversation
Lets a single build browse the Open edX platforms published by a site registry, re-theme to the chosen one, and sign in against it. Off by default (LMS_DIRECTORY.ENABLED=false); the app stays single-tenant when the flag is off. - core: LMSDirectoryConfig flag; Config.getApiHostURL() resolves to the selected LMS when enabled; CorePreferences.selectedBaseUrl/accentColor; lmsdirectory client (API/repo/models/Koin); LmsThemeController re-tints OpenEdXTheme to the platform's brand color - auth: LMS landing (Find my LMS / QR sign-in via ZXing), directory search/curated browse + manual entry, per-LMS theming on selection - profile: 'Report this LMS' bottom sheet (flag-gated) posting to the registry - app: flag-gated landing before sign-in; DI + startup wiring; seed theme from persisted accent on cold start - default_config: LMS_DIRECTORY block for dev/stage/prod - build: zxing-android-embedded for QR scanning
The catalog only returned summaries, so sign-in used the empty stock OAuth client and requests still hit the config host — login failed. Now: - fetch the full record on selection (LmsDetail): OAuth client id, feedback email, logo, accent; persist them in CorePreferences - Config.getOAuthClientId()/getFeedbackEmailAddress() honor the selected LMS - BaseUrlOverrideInterceptor rewrites every request to the selected host on the fly (the Retrofit client is built once, before selection) — the missing piece that let requests reach the chosen platform - SignIn shows the selected LMS logo + a 'Selected LMS / Change' banner - verified end-to-end: pick Sandbox -> branded sign-in -> logged into sandbox.openedx.org with the demo course loaded
- BaseUrlOverrideInterceptor routes requests to the selected host and is a no-op when nothing is selected (the mechanism that lets sign-in reach the platform). - LmsDetailDto maps the OAuth client id / feedback email / host that the catalog summary lacks, with blank values falling back correctly.
|
Thanks for the pull request, @IvanStepanok! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. 🔘 Update the status of your PRYour PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate. Where can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
Upstream openedx#420 replaced the browser-OAuth-code login with SAML SSO, deleting getAccessTokenFromCode / browserAuthCodeLogin / loginAuthCode / BrowserAuthHelper / GRANT_TYPE_CODE. The LMS Directory sign-in was built on that chain, so rather than resurrect what upstream removed, the directory now signs in through upstream's standard login against the re-pointed base URL: - Keep the directory feature: pre-login picker, per-LMS branding (accent, logo, login-background header), the 'Selected LMS / Change' banner, and Report this LMS. - Drop the browser-auth-code path: removed signInAuthCode + loginFailure and the authCode wiring in SignInFragment/AppActivity/DI; sign-in uses username/password (or SSO) against the selected host via BaseUrlOverrideInterceptor. - Config keeps both getLMSDirectoryConfig() and upstream's SSO getters. - Tests: stub Config.getLMSDirectoryConfig() in SignInViewModelTest; pass the new config arg in ProfileViewModelTest. Verified locally: compile, full testDevelopDebugUnitTest, and detektAll all pass.
Multi-tenant LMS Directory
https://openedx.atlassian.net/wiki/spaces/OEPM/pages/6532923408/Proposal+Multi-instance+configuration+for+the+Mobile+Apps
Lets a single build browse a directory of Open edX platforms, pick one, and sign in against it. When a learner selects a platform the app re-themes to that platform's accent color and branding, routes every API request to its host, and lands on its sign-in (or discovery). The whole feature sits behind the
LMS_DIRECTORYconfig flag, is disabled by default, and fails closed: with the flag off (or misconfigured) the Config accessors return the stock values and the app behaves exactly like today's single-tenant build.What's included
Directory & selection
LmsLandingFragmentshows the intro ("Find my LMS" / "Sign in with QR code") in search mode, or drops straight into the list in curated mode;SiteSelectionFragmenthosts the catalog list reached from thereDIRECTORY_MODE(search/curated) is OR-ed with the registry'sGET /api/v1/config—curated = remote== "curated". Insearchmode the field is a debounced (SEARCH_DEBOUNCE_MS) search box that doubles as URL entry; incuratedmode there is no search and the featuredSiteSelectionViewModeldrives aCatalogStatemachine (Idle,Loading,Loaded,Empty,Error) plus recent-history rows.LmsDirectoryRepository/LmsDirectoryApitalk toGET /api/v1/config,GET /api/v1/director(?q=search,?featured=true),andPOST /api/v1/reports— the same registry backend as the iOS app. There is no bundled sample catalog: the feature needs a realDIRECTORY_URL.Branding & sign-in
LmsThemeControlleris a ComposemutableStateOfholder for the selected platform'saccentColorloginBackgroundUrl.OpenEdXTnts the accent-driven palette via a privateAppColors.withAccent(...)(accent/info colors, outlined secondary-button text, etc.). No public Theme API signature changed.BaseUrlOverrideInterceptor: the Retrofit client is built once with the config host, anch request's scheme/host/port toCorePreferences.selectedBaseUrl. Nothing selected → requests pass through untouched.SignInViewgains the branded trm's logo, a "Selected LMS" row with a "Change" action back to the picker, and theLmsHeaderImageheader.SignUpViewandRestorePasswordFragmentswap theiLmsHeaderImage, which rendersloginBackgroundUrlwhen set and falls back to the stock header otherwise.MainFragment.applyLmsAccentTint()builds aColorStateListfrom the accent and applies it tobottomNavView(icon + text tint).QR sign-in
LmsQrScannerActivitywraps zxing'sDecoratedBarcodeView+CaptureManager(dependencycom.journeyapps:zxing-android-embedscanner with the framing viewfinder and a Close button, returning the scanned value throughScanContract.Report this LMS
ReportLmsSheetis a Material3ModalBottomSheet(drag handle,skipPartiallyExpanded) with 6categories (inappropriate,scam ', ' broken,other), a required note, and an optional base64 screenshot.ReportLmsViewModel.submit()posage, platform: "android"}(+optionalreporter_email/screenshot_base64) toPOST /api/v1/reports. The target is always the current platform (Config. getApiHost '). Reports go to the registry moderators, not to the LMS.Configuration & feature flag
LMS_DIRECTORYblock parsed NABLED,DIRECTORY_URL,DIRECTORY_MODE`).Config.getApiHostURL(),getOAuthClientId(), andgetFeedbackEmailAddress()consult the persisted selection only when `isRern the stock config values.CorePreferences(selectedBaseUrl,selectedLmsAccentColor,selectedOAuthClientId,selectedFgoUrl,selectedLmsLoginBackgroundUrl,selectedLmsTitle,lmsDirectoryCurated). `clearCorePreferences ' wipes them on logout.Connectivity & lifecycle
AppViewModel.isLmsSelectionRequired = getLMSDirectoryConfig().isReachable && selectedBaseUrl.isNullOrBlank(). ORLmsLandingFragmentwhen selection is required (and there's no pending auth code); once a platform is picked, launch goes straight to its sign-in.clearCorePreferences()); on restart,AppRouterclears the theme (LmsThemeController.clear()) and t` when the feature is reachable, and nothing is selected. A disabled/unreachable build leaves the app fully single-tenant.Tests
SiteSelectionViewModelTest(autry record and routes to discovery /to sign-in. -BaseUrlOverrideInterceptorTestelected LMS; passes through when nothing selected / selection blank. -LmsDetailDtoTest(app): DTO→domields fall back tobase_url/ null credentials.How to enable
Off by default. Set
ENABLED: trueand a realDIRECTORY_URLin the relevantconfig.yaml: