Skip to content

SDKS-5026: Device Authorization Grant Feature Testing#202

Open
vibhorgoswami wants to merge 3 commits into
developfrom
SDKS-5026
Open

SDKS-5026: Device Authorization Grant Feature Testing#202
vibhorgoswami wants to merge 3 commits into
developfrom
SDKS-5026

Conversation

@vibhorgoswami
Copy link
Copy Markdown
Contributor

@vibhorgoswami vibhorgoswami commented May 15, 2026

JIRA Ticket

SDKS-5026

Description

Introduces the test feature for Device Authorization Grant. Shows the option for QR code/User Code/Verification URL to authenticate the device.

Summary by CodeRabbit

  • New Features
    • Added Device Authorization Grant flow with QR code generation and verification-URI support
    • Multi-approval paths: DaVinci, Journey, or browser
    • Device authorization configuration UI: presets, custom configs, add/edit/duplicate/delete
    • Device session management and logout support for device-client sessions
    • Auth Grant token viewing and token operations integrated into token/profile screens

@vibhorgoswami vibhorgoswami self-assigned this May 15, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

PR changed again? Review this PR in Change Stack to compare snapshots and stay oriented.

Review Change Stack

Warning

Review limit reached

@vibhorgoswami, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 40 minutes and 9 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b68e3056-12c3-410e-a6ce-33accd891774

📥 Commits

Reviewing files that changed from the base of the PR and between 082a0fc and 454265d.

📒 Files selected for processing (1)
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt
📝 Walkthrough

Walkthrough

Adds a Device Authorization Grant flow to the sample app: ZXing dependency, device-auth SDK client and config, activation UI with QR and approval sheet, navigation for approval paths, token/userprofile/logout wiring, and Env UI for configuring device-auth presets/customs.

Changes

Device Authorization Grant Feature

Layer / File(s) Summary
Dependencies and SDK/client construction
gradle/libs.versions.toml, samples/pingsampleapp/build.gradle.kts, samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt
Adds ZXing QR dependency and version alias; introduces DeviceAuthConfigState, default preset, oidcDeviceClient holder, buildDeviceAuthClient, and DataStore persistence/serialization for applied/custom device-auth configs.
DeviceAuthorizationGrant ViewModel and Screen
samples/pingsampleapp/src/main/java/.../authgrant/DeviceAuthorizationGrantViewModel.kt, .../authgrant/DeviceAuthorizationGrantScreen.kt
Implements DeviceAuthorizationGrantViewModel managing device-flow polling and UI StateFlow; adds DeviceAuthorizationGrantScreen with Start/Verify UI, QR generation, copyable user code and verification URL, and modal approval sheet.
Env UI: Device Auth card and sheet
samples/pingsampleapp/src/main/java/.../config/Env.kt
Adds DeviceAuthCard listing presets/customs and DeviceAuthSheetContent form; wires sheet to EnvViewModel.saveCustomDeviceAuthConfig and bottom-sheet dispatcher.
Approve device screen and verification URI integration
samples/pingsampleapp/src/main/java/.../authgrant/ApproveDeviceScreen.kt, .../davinci/DaVinci.kt, .../davinci/DaVinciViewModel.kt, .../journey/JourneyViewModel.kt
Adds ApproveDeviceScreen for editable verification URI and three approval paths (DaVinci, Journey, browser). DaVinci/DaVinciViewModel and JourneyViewModel now accept optional verificationUri and pass VERIFICATION_URI_COMPLETE when present.
Navigation routes and device approval destinations
samples/pingsampleapp/src/main/java/.../navigation/Navigation.kt
Replaces static user-profile route with typed user_profile?type={type}; adds DEVICE_AUTHORIZATION_GRANT, DAVINCI_DEVICE_APPROVE, JOURNEY_DEVICE_APPROVAL, and journeyWithVerification route helpers; wires full device-authorization grant navigation and approval flows.
Token and UserProfile integration
samples/pingsampleapp/src/main/java/.../token/*, .../userprofile/*
Adds TokenType.AUTH_GRANT, extends TokenState with authGrantToken/authGrantError, implements authGrantAccessToken/authGrantRefresh/authGrantRevoke using oidcDeviceClient?.user(), adds AUTH_GRANT tab in Token UI, and extends UserProfile/UserProfileViewModel to load/display auth-grant user info.
Home and logout changes
samples/pingsampleapp/src/main/java/.../home/HomeApp.kt, .../logout/Logout.kt, .../logout/LogoutViewModel.kt
Adds Home row for Device Authorization Grant; LogoutViewModel tracks device-client session, adds logoutOidcDeviceClient, and includes device-client in logoutAll with runCatching/finally handling; UI shows device auth session logout option.
String resources
samples/pingsampleapp/src/main/res/values/strings.xml
Adds text_device_authorization_grant_title and text_device_authorization_grant_subtitle resources.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • spetrov
  • rodrigoareis
  • vahancouver
  • witrisna

Poem

🐰 I hopped through QR dots with a cheer,
A verification link held so near,
DaVinci and Journey nod with delight,
Tokens and profiles now shine bright,
Hooray — device auth springs to life tonight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: introducing a Device Authorization Grant feature testing capability in the sample app.
Description check ✅ Passed The PR description includes the required JIRA ticket reference and provides a clear, brief explanation of the changes (Device Authorization Grant test feature with QR code/user code/verification URL options).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SDKS-5026

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 44.44%. Comparing base (31e9f6e) to head (454265d).

Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #202      +/-   ##
=============================================
+ Coverage      44.42%   44.44%   +0.02%     
  Complexity      1393     1393              
=============================================
  Files            315      315              
  Lines           9623     9623              
  Branches        1474     1474              
=============================================
+ Hits            4275     4277       +2     
  Misses          4784     4784              
+ Partials         564      562       -2     
Flag Coverage Δ
integration-tests 28.68% <ø> (+0.01%) ⬆️
unit-tests 26.11% <ø> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt (1)

346-364: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Initialize Device Auth client during ViewModel bootstrap

daApplied is loaded and assigned (Line 360), but no SDK instance is built in the same init path. This can leave oidcDeviceClient out of sync with applied config until a manual selection happens.

Suggested fix
             withContext(Dispatchers.Main) {
                 customJourneyConfigs = jCustom
                 customDaVinciConfigs = dvCustom
                 customWebConfigs = wCustom
                 customDeviceAuthConfigs = daCustom
                 appliedJourneyConfig = jApplied
                 appliedDaVinciConfig = dvApplied
                 appliedWebConfig = wApplied
                 appliedDeviceAuthConfig = daApplied
                 buildJourneyInstance(jApplied)
                 buildDaVinciInstance(dvApplied)
                 buildWebInstance(wApplied)
+                buildDeviceAuthClient(daApplied)
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt`
around lines 346 - 364, The device auth applied config is loaded into
appliedDeviceAuthConfig but the SDK client isn't initialized during ViewModel
bootstrap; call the method that builds the Device Auth SDK instance (e.g.,
buildDeviceAuthInstance) with daApplied inside the same withContext block after
setting appliedDeviceAuthConfig so oidcDeviceClient is initialized and stays in
sync with the applied config.
🧹 Nitpick comments (1)
samples/pingsampleapp/src/main/res/values/strings.xml (1)

25-26: 💤 Low value

Consider using consistent terminology in title and subtitle.

The title uses the full term "Device Authorization Grant" while the subtitle abbreviates to "Device Auth". For improved clarity and consistency, consider using the full term in the subtitle as well.

📝 Suggested refinement
-    <string name="text_device_authorization_grant_subtitle">Start or verify Device Auth</string>
+    <string name="text_device_authorization_grant_subtitle">Start or verify Device Authorization</string>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@samples/pingsampleapp/src/main/res/values/strings.xml` around lines 25 - 26,
Update the subtitle string to use the full term to match the title: change the
value of the resource named text_device_authorization_grant_subtitle so it reads
"Start or verify Device Authorization Grant" (matching the wording of
text_device_authorization_grant_title) to keep terminology consistent across the
UI.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/ApproveDeviceScreen.kt`:
- Around line 207-211: The onClick handler for the Approve with Browser Button
calls context.startActivity(Intent(Intent.ACTION_VIEW,
initialVerificationUri.toUri())) without guarding for ActivityNotFoundException;
wrap the launch in a safe check (use
context.packageManager?.resolveActivity(intent, 0) to ensure an app can handle
the intent or run startActivity inside a try/catch for
ActivityNotFoundException) and only call
onApproveWithBrowser(initialVerificationUri) after a successful launch; on
failure, log or show a user-facing message instead of dismissing UI so the flow
doesn't crash.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/DeviceAuthorizationGrantViewModel.kt`:
- Around line 98-127: In the DeviceFlowStatus.Expired,
DeviceFlowStatus.AccessDenied and is DeviceFlowStatus.Failure branches update
the _uiState to also clear any stale device-auth artifacts: set userCode = ""
(or null if your UI state uses nullable), verificationUri = "" (or null), and
isSuccess = false along with the existing
hasStarted/statusMessage/errorMessage/isLoading changes so previous codes/URIs
and success flags are not retained after terminal error states; modify the calls
to _uiState.update in those branches (the ones shown handling Expired,
AccessDenied and Failure) to include these fields.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt`:
- Around line 470-472: The code compares appliedDeviceAuthConfig.display to
deleted.display and then injects a hard-coded DeviceAuthConfigState; instead,
compare a stable identifier (e.g., appliedDeviceAuthConfig.id or
appliedDeviceAuthConfig.issuer) to deleted.id/issuer and reuse an existing
preset from your presets list before falling back; update the branch around
appliedDeviceAuthConfig, deleted and selectDeviceAuthConfig to search your
presets for a matching config (by id or issuer) and call
selectDeviceAuthConfig(matchingPreset) if found, otherwise clear the selection
or select a safe default from presets rather than instantiating a new hard-coded
DeviceAuthConfigState.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/Logout.kt`:
- Around line 159-173: The aggregate session predicates that drive the “Logout
All” enabled state and the “No Active Sessions” message do not include the
device-auth session (state.oidcDeviceClient); update the aggregate checks (the
functions/expressions that compute whether any session is active and whether
“logout all” should be enabled—e.g., the code in the composable/logic that reads
state.oidcUserSession, state.oidcBackchannel, state.samlSession, etc.) to also
consider state.oidcDeviceClient, and ensure the same updated predicate is used
where the “No Active Sessions” label and the Logout All button enabled/disabled
logic are computed (update the related ViewModel/composable methods such as
listLogoutOptions(), the logout-all handler predicate, and the UI
visibility/enablement checks to include oidcDeviceClient).

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/LogoutViewModel.kt`:
- Around line 62-67: Wrap the logout calls in try/catch/finally so failures
don't prevent onCompleted() or subsequent logouts: in logoutOidcDeviceClient,
call oidcDeviceClient?.user()?.logout() inside a try block, log or handle the
exception in catch, and call onCompleted() in finally; do the same pattern for
the other logout helpers referenced in this file (e.g., the logoutAll sequence
and any methods around lines 69-77) so each logout is resilient and errors don't
stop later calls.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt`:
- Around line 115-122: The Tab composable currently hardcodes the label "Auth
Grant" (seen where Tab is created with selected = tokenState.selectedTab ==
TokenType.AUTH_GRANT and onClick calling
tokenViewModel.selectTab(TokenType.AUTH_GRANT) /
tokenViewModel.loadAllTokens()); extract this and other user-facing strings in
the Token composable (also around lines 153-206) into resources by adding
entries like auth_grant, access_token, clear, refresh, revoke to strings.xml and
replace hardcoded Text("Auth Grant") (and other hardcoded Text(...) usages) with
Text(stringResource(R.string.<name>)) to enable i18n consistency.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/userprofile/UserProfileViewModel.kt`:
- Around line 180-196: In authGrantUserInfo, when oidcDeviceClient?.user() is
null the function currently returns without clearing previous values; update the
logic in authGrantUserInfo to call state.update and set authGrantUser = null and
authGrantError = null (or appropriate cleared values) when
oidcDeviceClient?.user() returns null so stale profile data is removed; locate
this in the authGrantUserInfo function and ensure both the null-branch and
existing Result.Failure/Result.Success branches consistently update state via
state.update.

---

Outside diff comments:
In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt`:
- Around line 346-364: The device auth applied config is loaded into
appliedDeviceAuthConfig but the SDK client isn't initialized during ViewModel
bootstrap; call the method that builds the Device Auth SDK instance (e.g.,
buildDeviceAuthInstance) with daApplied inside the same withContext block after
setting appliedDeviceAuthConfig so oidcDeviceClient is initialized and stays in
sync with the applied config.

---

Nitpick comments:
In `@samples/pingsampleapp/src/main/res/values/strings.xml`:
- Around line 25-26: Update the subtitle string to use the full term to match
the title: change the value of the resource named
text_device_authorization_grant_subtitle so it reads "Start or verify Device
Authorization Grant" (matching the wording of
text_device_authorization_grant_title) to keep terminology consistent across the
UI.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 12e2be9e-a913-490c-b230-974b82dd3767

📥 Commits

Reviewing files that changed from the base of the PR and between dc3df43 and 4411680.

📒 Files selected for processing (19)
  • gradle/libs.versions.toml
  • samples/pingsampleapp/build.gradle.kts
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/ApproveDeviceScreen.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/DeviceAuthorizationGrantScreen.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/DeviceAuthorizationGrantViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/Env.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/DaVinci.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/DaVinciViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/home/HomeApp.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/Logout.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/LogoutViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/navigation/Navigation.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/TokenState.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/TokenViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/userprofile/UserProfile.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/userprofile/UserProfileViewModel.kt
  • samples/pingsampleapp/src/main/res/values/strings.xml

Comment on lines +115 to +122
Tab(
selected = tokenState.selectedTab == TokenType.AUTH_GRANT,
onClick = {
tokenViewModel.selectTab(TokenType.AUTH_GRANT)
tokenViewModel.loadAllTokens()
},
text = { Text("Auth Grant") }
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Externalize new/edited UI labels to resources.

New/edited user-facing labels are hardcoded in the composable. Please move them to strings.xml and use stringResource(...) for i18n consistency.

Proposed fix
+import androidx.compose.ui.res.stringResource
@@
-                    text = { Text("Auth Grant") }
+                    text = { Text(stringResource(R.string.auth_grant)) }
@@
-                            text = { Text("Refresh") },
+                            text = { Text(stringResource(R.string.refresh)) },
@@
-                            text = { Text("Revoke") },
+                            text = { Text(stringResource(R.string.revoke)) },
@@
-                        Text(text = "AccessToken")
+                        Text(text = stringResource(R.string.access_token))
@@
-                        Text(text = "Clear")
+                        Text(text = stringResource(R.string.clear))
@@
-                        Text(text = "Refresh")
+                        Text(text = stringResource(R.string.refresh))
@@
-                        Text(text = "Revoke")
+                        Text(text = stringResource(R.string.revoke))
<!-- samples/pingsampleapp/src/main/res/values/strings.xml -->
<string name="auth_grant">Auth Grant</string>
<string name="access_token">Access token</string>
<string name="clear">Clear</string>
<string name="refresh">Refresh</string>
<string name="revoke">Revoke</string>

Also applies to: 153-206

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt`
around lines 115 - 122, The Tab composable currently hardcodes the label "Auth
Grant" (seen where Tab is created with selected = tokenState.selectedTab ==
TokenType.AUTH_GRANT and onClick calling
tokenViewModel.selectTab(TokenType.AUTH_GRANT) /
tokenViewModel.loadAllTokens()); extract this and other user-facing strings in
the Token composable (also around lines 153-206) into resources by adding
entries like auth_grant, access_token, clear, refresh, revoke to strings.xml and
replace hardcoded Text("Auth Grant") (and other hardcoded Text(...) usages) with
Text(stringResource(R.string.<name>)) to enable i18n consistency.

Base automatically changed from SDKS-4784 to develop June 2, 2026 17:49
@vibhorgoswami vibhorgoswami requested a review from witrisna June 2, 2026 18:05
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (5)
samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt (1)

392-395: 💤 Low value

Missing buildDeviceAuthClient call for consistency with other config types.

The init block rebuilds Journey, DaVinci, and Web SDK instances from loaded configs, but does not rebuild the device auth client. While initConfigs() already builds it at app startup, the pattern here ensures ViewModel state and SDK instance are synchronized.

♻️ Suggested fix for consistency
                 buildJourneyInstance(jApplied)
                 buildDaVinciInstance(dvApplied)
                 buildWebInstance(wApplied)
+                buildDeviceAuthClient(daApplied)
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt`
around lines 392 - 395, The init block currently calls
buildJourneyInstance(jApplied), buildDaVinciInstance(dvApplied), and
buildWebInstance(wApplied) but omits rebuilding the device auth client; add a
call to buildDeviceAuthClient(dacApplied) (or the appropriate variable name used
for device-auth configs) alongside those other calls in the same init block so
the ViewModel state and SDK instance remain synchronized with initConfigs()’s
startup behavior.
samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt (4)

187-195: ⚡ Quick win

Externalize token field labels.

The token field labels ("Access Token", "Refresh Token", "ID Token", "Token Type", "Scope") are hardcoded. Move these to strings.xml for i18n consistency.

♻️ Proposed fix

Add to strings.xml:

<string name="token_label_access">Access Token</string>
<string name="token_label_refresh">Refresh Token</string>
<string name="token_label_id">ID Token</string>
<string name="token_label_type">Token Type</string>
<string name="token_label_scope">Scope</string>

Then update TokenCard:

+import androidx.compose.ui.res.stringResource
+import com.pingidentity.samples.pingsampleapp.R
@@
 `@Composable`
 private fun TokenCard(token: Token) {
-    TokenFieldCard(label = "Access Token", value = token.accessToken)
-    token.refreshToken?.let { TokenFieldCard(label = "Refresh Token", value = it) }
-    token.idToken?.let { TokenFieldCard(label = "ID Token", value = it) }
-    token.tokenType?.let { TokenFieldCard(label = "Token Type", value = it, truncate = false, copyable = false) }
-    token.scope?.let { TokenFieldCard(label = "Scope", value = it, truncate = false, copyable = false) }
+    TokenFieldCard(label = stringResource(R.string.token_label_access), value = token.accessToken)
+    token.refreshToken?.let { TokenFieldCard(label = stringResource(R.string.token_label_refresh), value = it) }
+    token.idToken?.let { TokenFieldCard(label = stringResource(R.string.token_label_id), value = it) }
+    token.tokenType?.let { TokenFieldCard(label = stringResource(R.string.token_label_type), value = it, truncate = false, copyable = false) }
+    token.scope?.let { TokenFieldCard(label = stringResource(R.string.token_label_scope), value = it, truncate = false, copyable = false) }
     ExpiryCountdownCard(token = token)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt`
around lines 187 - 195, The hardcoded labels in TokenCard should be moved to
resources: add the five strings (token_label_access, token_label_refresh,
token_label_id, token_label_type, token_label_scope) to strings.xml and update
TokenCard to fetch and pass those localized strings to TokenFieldCard (and keep
truncate/copyable flags as-is); locate and modify the TokenCard composable and
any calls to TokenFieldCard/ExpiryCountdownCard to use
stringResource(R.string.token_label_...) for each label so the UI is
internationalized.

220-226: ⚡ Quick win

Externalize countdown display strings.

The "Expired" status text (line 220) and "Expires In" label (line 226) are hardcoded and should be moved to strings.xml for i18n consistency.

♻️ Proposed fix

Add to strings.xml:

<string name="token_expired">Expired</string>
<string name="token_expires_in">Expires In</string>

Then update:

+import androidx.compose.ui.res.stringResource
+import com.pingidentity.samples.pingsampleapp.R
@@
-    val countdownText = if (expired) "Expired"
+    val countdownText = if (expired) stringResource(R.string.token_expired)
@@
     TokenFieldCard(
-        label = "Expires In",
+        label = stringResource(R.string.token_expires_in),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt`
around lines 220 - 226, Move the hardcoded UI strings into resource strings:
replace the "Expired" literal used in countdownText and the "Expires In" literal
passed to TokenFieldCard with references to string resources (e.g.,
R.string.token_expired and R.string.token_expires_in). Add the two entries
token_expired and token_expires_in to strings.xml, then update the code paths
that set countdownText and the TokenFieldCard label to fetch
context.getString(R.string.token_expired) and
context.getString(R.string.token_expires_in) (or use stringResource in Compose)
so the UI uses the new resources.

80-108: ⚡ Quick win

Externalize contentDescription strings for accessibility and i18n.

The contentDescription values for the TopAppBar action icons are hardcoded. These accessibility strings should be externalized to strings.xml for consistency with i18n best practices.

♻️ Proposed fix

Add to strings.xml:

<string name="content_desc_access_token">Access Token</string>
<string name="content_desc_refresh">Refresh</string>
<string name="content_desc_revoke">Revoke</string>
<string name="content_desc_clear">Clear</string>

Then update the code:

+import androidx.compose.ui.res.stringResource
+import com.pingidentity.samples.pingsampleapp.R
@@
-                            contentDescription = "Access Token",
+                            contentDescription = stringResource(R.string.content_desc_access_token),
@@
-                            contentDescription = "Refresh",
+                            contentDescription = stringResource(R.string.content_desc_refresh),
@@
-                            contentDescription = "Revoke",
+                            contentDescription = stringResource(R.string.content_desc_revoke),
@@
-                            contentDescription = "Clear",
+                            contentDescription = stringResource(R.string.content_desc_clear),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt`
around lines 80 - 108, Replace hardcoded contentDescription literals on the
TopAppBar action IconButton calls with resource lookups: add string resources
for "Access Token", "Refresh", "Revoke", and "Clear" (e.g.,
content_desc_access_token, content_desc_refresh, content_desc_revoke,
content_desc_clear) in strings.xml and then use stringResource(...) for the
contentDescription parameter in the Icon composables inside the IconButton
callbacks (the ones invoking tokenViewModel.accessToken(),
tokenViewModel.refresh(), tokenViewModel.revoke(), tokenViewModel.reset()) so
that accessibility text is externalized for i18n.

308-321: ⚡ Quick win

Externalize empty state message template.

The empty state message template can be externalized using a string resource with a placeholder for i18n support.

♻️ Proposed fix

Add to strings.xml:

<string name="token_empty_message">No %s token available</string>

Then update:

+import androidx.compose.ui.res.stringResource
+import com.pingidentity.samples.pingsampleapp.R
@@
         Text(
-            text = "No ${tab.name.lowercase().replaceFirstChar { it.uppercase() }} token available",
+            text = stringResource(
+                R.string.token_empty_message,
+                tab.name.lowercase().replaceFirstChar { it.uppercase() }
+            ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt`
around lines 308 - 321, Replace the hardcoded empty-state text in the EmptyCard
composable with a string resource that has a placeholder (e.g.
token_empty_message = "No %s token available") to enable i18n; in EmptyCard
(which takes TokenType tab) call stringResource(...) and pass the formatted tab
name (keeping the same lowercase/uppercase transform you currently use on
tab.name) instead of the inline string, and ensure you add the new string to
strings.xml and import androidx.compose.ui.res.stringResource.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt`:
- Around line 392-395: The init block currently calls
buildJourneyInstance(jApplied), buildDaVinciInstance(dvApplied), and
buildWebInstance(wApplied) but omits rebuilding the device auth client; add a
call to buildDeviceAuthClient(dacApplied) (or the appropriate variable name used
for device-auth configs) alongside those other calls in the same init block so
the ViewModel state and SDK instance remain synchronized with initConfigs()’s
startup behavior.

In
`@samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt`:
- Around line 187-195: The hardcoded labels in TokenCard should be moved to
resources: add the five strings (token_label_access, token_label_refresh,
token_label_id, token_label_type, token_label_scope) to strings.xml and update
TokenCard to fetch and pass those localized strings to TokenFieldCard (and keep
truncate/copyable flags as-is); locate and modify the TokenCard composable and
any calls to TokenFieldCard/ExpiryCountdownCard to use
stringResource(R.string.token_label_...) for each label so the UI is
internationalized.
- Around line 220-226: Move the hardcoded UI strings into resource strings:
replace the "Expired" literal used in countdownText and the "Expires In" literal
passed to TokenFieldCard with references to string resources (e.g.,
R.string.token_expired and R.string.token_expires_in). Add the two entries
token_expired and token_expires_in to strings.xml, then update the code paths
that set countdownText and the TokenFieldCard label to fetch
context.getString(R.string.token_expired) and
context.getString(R.string.token_expires_in) (or use stringResource in Compose)
so the UI uses the new resources.
- Around line 80-108: Replace hardcoded contentDescription literals on the
TopAppBar action IconButton calls with resource lookups: add string resources
for "Access Token", "Refresh", "Revoke", and "Clear" (e.g.,
content_desc_access_token, content_desc_refresh, content_desc_revoke,
content_desc_clear) in strings.xml and then use stringResource(...) for the
contentDescription parameter in the Icon composables inside the IconButton
callbacks (the ones invoking tokenViewModel.accessToken(),
tokenViewModel.refresh(), tokenViewModel.revoke(), tokenViewModel.reset()) so
that accessibility text is externalized for i18n.
- Around line 308-321: Replace the hardcoded empty-state text in the EmptyCard
composable with a string resource that has a placeholder (e.g.
token_empty_message = "No %s token available") to enable i18n; in EmptyCard
(which takes TokenType tab) call stringResource(...) and pass the formatted tab
name (keeping the same lowercase/uppercase transform you currently use on
tab.name) instead of the inline string, and ensure you add the new string to
strings.xml and import androidx.compose.ui.res.stringResource.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6aab32e6-574a-4e58-8b8f-3cae53c03a48

📥 Commits

Reviewing files that changed from the base of the PR and between 71798b2 and 082a0fc.

📒 Files selected for processing (20)
  • gradle/libs.versions.toml
  • samples/pingsampleapp/build.gradle.kts
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/ApproveDeviceScreen.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/DeviceAuthorizationGrantScreen.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/DeviceAuthorizationGrantViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/Env.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/EnvViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/DaVinci.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/DaVinciViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/home/HomeApp.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/journey/JourneyViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/Logout.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/LogoutViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/navigation/Navigation.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/Token.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/TokenState.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/TokenViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/userprofile/UserProfile.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/userprofile/UserProfileViewModel.kt
  • samples/pingsampleapp/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (1)
  • gradle/libs.versions.toml
🚧 Files skipped from review as they are similar to previous changes (17)
  • samples/pingsampleapp/src/main/res/values/strings.xml
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/home/HomeApp.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/DaVinci.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/TokenState.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/userprofile/UserProfile.kt
  • samples/pingsampleapp/build.gradle.kts
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/DaVinciViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/journey/JourneyViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/Logout.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/ApproveDeviceScreen.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/token/TokenViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/logout/LogoutViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/DeviceAuthorizationGrantViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/config/Env.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/userprofile/UserProfileViewModel.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/authgrant/DeviceAuthorizationGrantScreen.kt
  • samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/navigation/Navigation.kt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants