Skip to content

ADFA-3644 | Support pop-out tooltip display for tablets and DeX#1164

Merged
jatezzz merged 2 commits intostagefrom
feat/ADFA-3644-help-activity-multi-window
Apr 9, 2026
Merged

ADFA-3644 | Support pop-out tooltip display for tablets and DeX#1164
jatezzz merged 2 commits intostagefrom
feat/ADFA-3644-help-activity-multi-window

Conversation

@jatezzz
Copy link
Copy Markdown
Collaborator

@jatezzz jatezzz commented Apr 8, 2026

Description

Refactored ToolTipManager and HelpActivity to support opening tooltip help in a separate, adjacent window when the user is on a tablet or Samsung DeX device.

  • Created DeviceFormFactorUtils to reliably detect large screens and DeX modes, utilizing physical screen metrics and Samsung's hidden APIs to maintain accurate detection even if the primary app window is resized.
  • Encapsulated the launch logic within HelpActivity.
  • Implemented SINGLE_TOP with a static URI to recycle the secondary window, preventing the creation of infinite windows when users click multiple tooltips sequentially.

Details

document_5093759534346798852.mp4

Ticket

ADFA-3644

Observation

The DeviceFormFactorUtils uses maximumWindowMetrics (or getRealMetrics on legacy) and reflection for Samsung DeX to guarantee the application doesn't incorrectly fall back to the single-window mobile layout when the user shrinks the IDE window in a desktop environment.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough
  • Multi-window tooltip support: tooltip help can open in a separate, adjacent window on large-screen devices (tablets and Samsung DeX).
  • New DeviceFormFactorUtils: central utility that detects large screens and DeX modes using:
    • maximumWindowMetrics (Android R+) and getRealMetrics fallback for physical metrics.
    • smallestScreenWidthDp with a 500dp threshold and a physical-size fallback for tablet detection.
    • Samsung DeX detection via uiMode desk type, a reflected "desktopmode" system service path, and a legacy reflection path against Configuration fields.
    • All reflection and metric code is defensive — failures return false rather than throwing.
  • Refactored HelpActivity:
    • Added HelpActivity.launch(context, url, title) to centralize launch behavior.
    • For large-screen-like devices, launch uses Intent.flags: NEW_TASK | NEW_DOCUMENT | SINGLE_TOP | LAUNCH_ADJACENT and sets a static data URI (cogo-help://tooltip/active-window) to enable secondary-window reuse.
    • Implements onNewIntent(...) + updateUIFromIntent(...) to refresh title and WebView content when the existing help activity instance is reused (prevents multiple secondary windows).
    • Conditionally adds FLAG_ACTIVITY_NEW_TASK when launching from non-Activity contexts.
  • ToolTipManager simplified to call HelpActivity.launch(...) (removed manual Intent construction and extra flags/imports).
  • WebView behavior: HelpActivity configures WebView with JavaScript, file access, universal file access, DOM storage, database enabled, and allows mixed content.

Risks / Best-practice considerations:

  • Reflection-based Samsung DeX detection is fragile and OEM-specific; hidden API changes may break detection. Current approach swallows failures (returns false) and may make regressions harder to detect without logging/telemetry.
  • Reliance on a static URI + SINGLE_TOP and multi-window launch flags assumes no other component will conflict with that URI or launch mode; collisions or unexpected intent routing could cause stale or wrong content to appear.
  • Reusing the same activity instance requires careful state management; onNewIntent must fully replace UI state to avoid showing stale/mixed content when tooltips are opened rapidly.
  • Physical-metrics and reflection checks add complexity and runtime cost; repeated heavy metric computations or reflection could impact performance on some devices.
  • Silent fallbacks: detection failures default to non-large-screen behavior without observable diagnostics — consider adding logging/telemetry for future debugging.
  • WebView configuration increases attack surface (enabled JS, file access, universal access, mixed content). Ensure content sources are trusted and consider narrowing permissions or sanitizing content.

High-level code changes:

  • Added DeviceFormFactor (data class) and DeviceFormFactorUtils.getCurrent(context).
  • Modified HelpActivity: launch(), Intent flags/data, onNewIntent(), updateUIFromIntent(), and WebView setup.
  • Updated TooltipManager to invoke HelpActivity.launch(...) for help links.

Testing recommendations:

  • Manual and automated testing on tablets and Samsung DeX (modern and legacy paths) to validate device detection and multi-window behavior.
  • Test rapid sequential tooltip opens to confirm activity reuse and correct UI updates.
  • Add logging/telemetry around reflection/metric failures and intent routing to aid debugging.
  • Review WebView usage for security: validate content origins and consider reducing risky settings where possible.

Walkthrough

Adds a DeviceFormFactor utility and updates HelpActivity to support a companion launch(...) and runtime intent updates; ToolTipManager now calls HelpActivity.launch(...) instead of constructing Intents itself.

Changes

Cohort / File(s) Summary
Device Form Factor Detection
common/src/main/java/com/itsaky/androidide/utils/DeviceFormFactor.kt
New DeviceFormFactor data class and DeviceFormFactorUtils.getCurrent(context) to detect isTablet and Samsung/desktop DEX modes with guarded reflection and display-metrics fallbacks.
HelpActivity Intent Handling
common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt
Added companion fun launch(context, url, title) that sets intent data/extras and conditional flags using isLargeScreenLike; added onNewIntent() and updateUIFromIntent() to refresh action bar title and WebView from incoming intents.
Tooltip Integration
idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt
Replaced manual Intent creation with HelpActivity.launch(context, url, title) in tooltip help link handler; removed unused imports and standardized error logging to TAG.

Sequence Diagram(s)

sequenceDiagram
    participant User as "User"
    participant Tip as "ToolTipManager"
    participant Help as "HelpActivity"
    participant FF as "DeviceFormFactorUtils"
    participant System as "Android System"

    User->>Tip: click help link (url, title)
    Tip->>Help: launch(context, url, title)
    Help->>FF: getCurrent(context)
    FF->>System: query uiMode / resources / metrics / Samsung services
    System-->>FF: device metrics / flags
    FF-->>Help: DeviceFormFactor(isTablet, isDexMode)
    Help->>Help: build Intent (set data/extras, conditional flags)
    Help->>System: startActivity(intent)
    System->>Help: onCreate()/onNewIntent(intent)
    Help->>Help: updateUIFromIntent(intent) -> set title, load WebView URL
    Help-->>User: render help content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • itsaky-adfa
  • Daniel-ADFA
  • dara-abijo-adfa

Poem

🐰 I hop from tip to helpful page,
Flags set kindly for every gauge.
Tablet, Dex — I sniff the air,
Intent refreshed with loving care,
WebView sings; the user grins.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding support for pop-out tooltip display on tablets and DeX devices, which is the core objective of the PR.
Description check ✅ Passed The description is directly related to the changeset, explaining the refactoring of ToolTipManager and HelpActivity to support tooltips on tablets and DeX, with details about DeviceFormFactorUtils and SINGLE_TOP implementation.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-3644-help-activity-multi-window

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.

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: 1

Caution

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

⚠️ Outside diff range comments (1)
common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt (1)

92-145: ⚠️ Potential issue | 🟠 Major

Remove the duplicate initial loadUrl() call and update the title in onNewIntent().

onCreate() loads CONTENT_KEY twice: once at line 134 via htmlContent?.let { webView.loadUrl(url) }, then again at line 144 via loadUrlFromIntent(intent). This creates duplicate WebView history entries.

Additionally, onNewIntent() reloads content but does not refresh the action bar title when a new CONTENT_TITLE_KEY arrives, leaving stale titles on multi-window activity reuse.

🧭 Suggested fix
             val pageTitle = intent.getStringExtra(CONTENT_TITLE_KEY)
-            val htmlContent = intent.getStringExtra(CONTENT_KEY)

             supportActionBar?.title = pageTitle ?: getString(R.string.help)

             // Configure WebView settings for localhost access
@@ -130,10 +128,6 @@
                 }
             }

-            // Load the HTML file from the assets folder
-            htmlContent?.let { url ->
-                webView.loadUrl(url)
-            }
         }

         // Set up back navigation callback for system back button
@@ -147,6 +141,9 @@
     override fun onNewIntent(intent: Intent) {
         super.onNewIntent(intent)
         setIntent(intent)
+        val pageTitle = intent.getStringExtra(CONTENT_TITLE_KEY)
+        supportActionBar?.title = pageTitle ?: getString(R.string.help)
         loadUrlFromIntent(intent)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt`
around lines 92 - 145, Remove the duplicate WebView navigation by only loading
the URL via loadUrlFromIntent(intent) (avoid the htmlContent?.let {
webView.loadUrl(url) } call in onCreate) so the CONTENT_KEY is not loaded twice
into history; also update onNewIntent(intent) to call supportActionBar?.title =
intent.getStringExtra(CONTENT_TITLE_KEY) (and then call
loadUrlFromIntent(intent) / handle CONTENT_KEY) so the action bar title is
refreshed when a new CONTENT_TITLE_KEY arrives. Use the existing helper
loadUrlFromIntent and the onNewIntent override to centralize both title and URL
updates.
🧹 Nitpick comments (1)
common/src/main/java/com/itsaky/androidide/utils/DeviceFormFactor.kt (1)

49-120: Don’t swallow every failure in form-factor detection.

These helpers currently convert any unexpected error into false via catch (_: Exception) and runCatching. Because this result controls whether the adjacent-window flow is used, silent fallback here makes platform regressions look like normal phone behavior instead of surfacing them.

🔧 Suggested direction

Catch only the reflective/lookup failures you expect here, e.g. ReflectiveOperationException, SecurityException, and ClassCastException, and replace runCatching in readSamsungDesktopModeValue() with an explicit fallback so unexpected failures still fail fast during development.

Based on learnings, Kotlin code in this repo should catch only the specific exception types expected from the runtime failure being handled.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/src/main/java/com/itsaky/androidide/utils/DeviceFormFactor.kt` around
lines 49 - 120, The helpers (isPhysicalLargeScreen, isSamsungDexModeModern,
isSamsungDexModeLegacy and readSamsungDesktopModeValue) currently swallow all
Exceptions; change them to only catch the expected reflection/lookup/security
errors (for example ReflectiveOperationException, SecurityException,
ClassCastException) and let other exceptions propagate. Replace the blanket
try/catch in isPhysicalLargeScreen and isSamsungDexModeModern with try blocks
that catch those specific exception types and return false only for them; update
readSamsungDesktopModeValue to remove runCatching/recoverCatching and instead
perform the explicit getField(...) then fallback to getDeclaredField(...) inside
targeted catch clauses that return null only for the expected
reflection/security exceptions. Ensure you reference and modify the functions
named isPhysicalLargeScreen, isSamsungDexModeModern, isSamsungDexModeLegacy, and
readSamsungDesktopModeValue.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt`:
- Around line 147-156: onNewIntent currently calls loadUrlFromIntent but never
updates the action-bar title; modify loadUrlFromIntent (or its call-site) to
also read the title extra from the intent (e.g.
intent.getStringExtra(TITLE_KEY)) and update the UI by calling
supportActionBar?.title = title (or setTitle(title) / binding.toolbar.title =
title) so the action-bar reflects the newly loaded content when onNewIntent is
used.

---

Outside diff comments:
In
`@common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt`:
- Around line 92-145: Remove the duplicate WebView navigation by only loading
the URL via loadUrlFromIntent(intent) (avoid the htmlContent?.let {
webView.loadUrl(url) } call in onCreate) so the CONTENT_KEY is not loaded twice
into history; also update onNewIntent(intent) to call supportActionBar?.title =
intent.getStringExtra(CONTENT_TITLE_KEY) (and then call
loadUrlFromIntent(intent) / handle CONTENT_KEY) so the action bar title is
refreshed when a new CONTENT_TITLE_KEY arrives. Use the existing helper
loadUrlFromIntent and the onNewIntent override to centralize both title and URL
updates.

---

Nitpick comments:
In `@common/src/main/java/com/itsaky/androidide/utils/DeviceFormFactor.kt`:
- Around line 49-120: The helpers (isPhysicalLargeScreen,
isSamsungDexModeModern, isSamsungDexModeLegacy and readSamsungDesktopModeValue)
currently swallow all Exceptions; change them to only catch the expected
reflection/lookup/security errors (for example ReflectiveOperationException,
SecurityException, ClassCastException) and let other exceptions propagate.
Replace the blanket try/catch in isPhysicalLargeScreen and
isSamsungDexModeModern with try blocks that catch those specific exception types
and return false only for them; update readSamsungDesktopModeValue to remove
runCatching/recoverCatching and instead perform the explicit getField(...) then
fallback to getDeclaredField(...) inside targeted catch clauses that return null
only for the expected reflection/security exceptions. Ensure you reference and
modify the functions named isPhysicalLargeScreen, isSamsungDexModeModern,
isSamsungDexModeLegacy, and readSamsungDesktopModeValue.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8a7a6e33-dc5d-4c18-b36f-eecb0419273f

📥 Commits

Reviewing files that changed from the base of the PR and between d300cff and 3a1734a.

📒 Files selected for processing (3)
  • common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt
  • common/src/main/java/com/itsaky/androidide/utils/DeviceFormFactor.kt
  • idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt

@jatezzz
Copy link
Copy Markdown
Collaborator Author

jatezzz commented Apr 8, 2026

Current behavior

document_5093759534346798867.mp4

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.

Caution

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

⚠️ Outside diff range comments (1)
common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt (1)

92-95: ⚠️ Potential issue | 🟡 Minor

Remove duplicate title/URL handling to avoid loading content twice.

The new updateUIFromIntent(intent) call at line 144 duplicates work already done earlier in onCreate:

  • Title is set at line 95, then again in updateUIFromIntent (line 155)
  • webView.loadUrl(url) is called at line 134, then again in updateUIFromIntent (line 158)

The double loadUrl() call will trigger two network requests for the same content.

Proposed fix: remove duplicate logic and rely on updateUIFromIntent
         with(binding) {
             setSupportActionBar(toolbar)
             supportActionBar!!.setDisplayHomeAsUpEnabled(true)
             toolbar.setNavigationOnClickListener { handleBackNavigation() }

             // Set status bar icons to be dark in light mode and light in dark mode
             WindowCompat.getInsetsController(this@HelpActivity.window, this@HelpActivity.window.decorView).apply {
                 isAppearanceLightStatusBars = !isSystemInDarkMode()
                 isAppearanceLightNavigationBars = !isSystemInDarkMode()
             }

-            val pageTitle = intent.getStringExtra(CONTENT_TITLE_KEY)
-            val htmlContent = intent.getStringExtra(CONTENT_KEY)
-
-            supportActionBar?.title = pageTitle ?: getString(R.string.help)

             // Configure WebView settings for localhost access
             webView.settings.javaScriptEnabled = true
             webView.settings.allowFileAccess = true
             webView.settings.allowFileAccessFromFileURLs = true
             webView.settings.allowUniversalAccessFromFileURLs = true
             webView.settings.domStorageEnabled = true
             webView.settings.databaseEnabled = true
             webView.settings.mixedContentMode = android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW

             // Set WebViewClient to handle page navigation within the WebView
             webView.webViewClient = object : WebViewClient() {
                 // ... unchanged ...
             }
-
-            // Load the HTML file from the assets folder
-            htmlContent?.let { url ->
-                webView.loadUrl(url)
-            }
         }

         // Set up back navigation callback for system back button
         onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
             override fun handleOnBackPressed() {
                 handleBackNavigation()
             }
         })
         updateUIFromIntent(intent)
     }

Also applies to: 132-135, 144-145

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt`
around lines 92 - 95, In onCreate, remove the redundant title and initial load
code so updateUIFromIntent(intent) is the single source of truth: delete the
retrieval/assignment of pageTitle/htmlContent, the supportActionBar?.title = ...
call and the webView.loadUrl(url) that occur before updateUIFromIntent(intent);
leave updateUIFromIntent(intent) to set supportActionBar?.title and call
webView.loadUrl(url) (ensure updateUIFromIntent handles null/defaults), so
updateUIFromIntent(intent) is the only place that sets the ActionBar title and
loads the WebView URL.
🧹 Nitpick comments (1)
common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt (1)

44-64: Consider documenting or enforcing Activity context requirement for accurate form factor detection.

The launch() method accepts a generic Context, but DeviceFormFactorUtils.getCurrent(context) relies on WindowManager metrics that may be inaccurate when passed an Application context instead of an Activity context (per the implementation in DeviceFormFactor.kt which uses windowManager.maximumWindowMetrics).

If an Application context is passed, large-screen detection could be incorrect, causing the wrong launch flags to be applied. Consider either:

  1. Adding a KDoc comment noting the requirement for Activity context for accurate detection
  2. Using a method signature that takes Activity when called from tooltip contexts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt`
around lines 44 - 64, The HelpActivity.launch method currently accepts a generic
Context but uses DeviceFormFactorUtils.getCurrent(context) which can return
incorrect metrics for non-Activity contexts; update launch to either require an
Activity parameter (change signature to launch(activity: Activity, url: String,
title: String) and use that activity when calling
DeviceFormFactorUtils.getCurrent) or, if you must keep Context, add a KDoc to
launch documenting that an Activity Context is required for correct form-factor
detection and add a runtime guard that treats non-Activity contexts as non-large
(e.g., if (context is Activity) use getCurrent(context) else assume small
screen) so flags are not wrongly applied; reference HelpActivity.launch and
DeviceFormFactorUtils.getCurrent when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt`:
- Around line 92-95: In onCreate, remove the redundant title and initial load
code so updateUIFromIntent(intent) is the single source of truth: delete the
retrieval/assignment of pageTitle/htmlContent, the supportActionBar?.title = ...
call and the webView.loadUrl(url) that occur before updateUIFromIntent(intent);
leave updateUIFromIntent(intent) to set supportActionBar?.title and call
webView.loadUrl(url) (ensure updateUIFromIntent handles null/defaults), so
updateUIFromIntent(intent) is the only place that sets the ActionBar title and
loads the WebView URL.

---

Nitpick comments:
In
`@common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt`:
- Around line 44-64: The HelpActivity.launch method currently accepts a generic
Context but uses DeviceFormFactorUtils.getCurrent(context) which can return
incorrect metrics for non-Activity contexts; update launch to either require an
Activity parameter (change signature to launch(activity: Activity, url: String,
title: String) and use that activity when calling
DeviceFormFactorUtils.getCurrent) or, if you must keep Context, add a KDoc to
launch documenting that an Activity Context is required for correct form-factor
detection and add a runtime guard that treats non-Activity contexts as non-large
(e.g., if (context is Activity) use getCurrent(context) else assume small
screen) so flags are not wrongly applied; reference HelpActivity.launch and
DeviceFormFactorUtils.getCurrent when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eeb2fdd1-b3a2-4a21-b187-e39d01fd71e8

📥 Commits

Reviewing files that changed from the base of the PR and between 3a1734a and 4bfca2f.

📒 Files selected for processing (1)
  • common/src/main/java/com/itsaky/androidide/activities/editor/HelpActivity.kt

jatezzz added 2 commits April 9, 2026 16:30
Refactors HelpActivity launch logic and adds device form factor detection
@jatezzz jatezzz force-pushed the feat/ADFA-3644-help-activity-multi-window branch from 4bfca2f to 7b6f5fe Compare April 9, 2026 21:30
@jatezzz jatezzz merged commit 0975600 into stage Apr 9, 2026
2 checks passed
@jatezzz jatezzz deleted the feat/ADFA-3644-help-activity-multi-window branch April 9, 2026 21:36
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.

2 participants