Skip to content

[WOOMOB-2784] Let POS discover phone card readers without Bluetooth - #16406

Merged
samiuelson merged 6 commits into
trunkfrom
issue/WOOMOB-2784-skip-bt-gates-for-phone-reader
Aug 14, 2026
Merged

[WOOMOB-2784] Let POS discover phone card readers without Bluetooth#16406
samiuelson merged 6 commits into
trunkfrom
issue/WOOMOB-2784-skip-bt-gates-for-phone-reader

Conversation

@kidinov

@kidinov kidinov commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes WOOMOB-2784

POS gated all card reader discovery behind Bluetooth scan/connect permissions, Bluetooth being on, fine location and location services. Phone card readers are found over Wi-Fi (NSD) and need none of that, so a merchant who only wanted to use a phone hit a Bluetooth prompt that has no close button, and their phone was never discovered. On a tablet without a Bluetooth radio it is a dead end — the "Enable Bluetooth" button has no adapter to enable.

Discovery now always runs the Wi-Fi side; the Bluetooth requirement only decides whether the Bluetooth branch starts. The prompts themselves are unchanged for merchants who need Bluetooth — they still block, and they still appear when no phone is around — but a discovered phone now takes over from them, and they offer a way into the phone-reader explainer, which was previously unreachable from that screen. Once a phone has been connected once, the prompt is skipped.

They were kept as blocking screens rather than downgraded to an inline hint because Bluetooth is by far the common flow, and for those merchants the prompt is the correct instruction, not noise.

Two related robustness fixes came out of this: a Bluetooth discovery failure no longer tears down Wi-Fi discovery with it, and discoverReaders() is now called inside the caught flow, since it throws eagerly when Terminal is not initialised.

Test Steps

Needs a tablet and a phone signed into the same store on the same Wi-Fi.

  1. On the tablet, revoke the app's Nearby devices and Location permissions, and turn Bluetooth off.
  2. Open POS → Connect your reader. The Bluetooth prompt appears exactly as before, now with a Use an Android phone as a card reader link.
  3. Tap that link → the phone-reader explainer opens. Dismiss it → you return to the prompt.
  4. On the phone, go to Menu → Payments → Card Reader Mode.
  5. Within a few seconds the tablet swaps from the prompt to the reader list showing the phone, plus an Allow Bluetooth to see card readers row.
  6. Tap Connect → tablet shows "Reader connected", phone shows "Paired with tablet".
  7. Close Card Reader Mode on the phone while the tablet is showing it in the list → the Bluetooth prompt comes back rather than scanning forever.
  8. Regression: grant the permissions, turn Bluetooth on, and confirm a Bluetooth reader still discovers and connects as before.

Images/gif

Bluetooth off, no phone nearby Phone starts advertising
Connected, Bluetooth still off Phone side
  • I have considered if this change warrants release notes and have added them to RELEASE-NOTES.txt if necessary. Use the "[Internal]" label for non-user-facing changes.

Phone readers are found over Wi-Fi, so the Bluetooth prerequisites no longer
gate discovery. The Bluetooth prompts stay for Bluetooth merchants, and a
discovered phone takes over from them.
@kidinov kidinov added type: enhancement A request for an enhancement. feature: point of sale POS project labels Aug 11, 2026
@kidinov kidinov added this to the 25.5 milestone Aug 11, 2026
@dangermattic

dangermattic commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

@wpmobilebot

wpmobilebot commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

App Icon📲 You can test the changes from this Pull Request in WooCommerce Android by scanning the QR code below to install the corresponding build.

App NameWooCommerce Android
Platform📱 Mobile
FlavorJalapeno
Build TypeDebug
Build Number778
Version25.4-rc-1
Application IDcom.woocommerce.android.prealpha
Commit05f258d
Installation URL7j9d3dve5eeu0
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@kidinov
kidinov requested a review from samiuelson August 11, 2026 12:44
@kidinov
kidinov marked this pull request as ready for review August 11, 2026 12:45

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Code Review - Found 2 potential issues

PR housekeeping
  • Applied repo POS guidance from AGENTS.md: the change stays within the POS card-reader connection controller/state/dialog and follows POS conventions.

Automatic review · claude-opus-4-8 · Workflow run

How to reply to a finding

Reply on this review (or inline at the line the finding refers to) with one of:

  • @claude addressed - I made the change. Bot verifies against the next diff before marking resolved.
  • @claude rejected: <reason> - Will not fix; reason gets quoted on the next review.
  • @claude not-applicable - Finding does not apply (wrong file, already covered elsewhere, etc.).

The bot honours these on the next review pass.

unmetBluetoothRequirement?.let { requirement ->
WooPosCardReaderConnectionState.BluetoothUnavailable(
requirement = requirement,
onFixClicked = { _state.value = requirement.toBlockingState() },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Code Review [follow-up]

Issue: When phones are shown with the "Bluetooth unavailable" strip, tapping it sets the state to a blocking requirement screen (e.g. BluetoothDisabled) but the phone discoveryJob keeps running. The next NSD ReadersFound snapshot (a phone re-registering with a new session, or another phone appearing) flows into handleReadersFound, whose 1/else branches unconditionally set ReaderFound/MultipleReadersFound and bounce the user off the fix screen. Note the asymmetry: the Failed branch already guards with !isShowingBluetoothRequirement(), but the readers-found path does not — so a merchant can struggle to actually reach the enable/permission screen from the strip.

Suggestion: Cancel discoveryJob when navigating to the blocking state from onFixClicked (mirroring how showRemoteTapToPayExplainer cancels discovery before switching state); discovery is restarted anyway via checkRequirementsAndStartDiscovery() once the requirement is satisfied.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, fixed in 05f258d. onFixClicked now cancels discoveryJob before switching to the blocking state, so a later NSD snapshot cannot bounce the merchant off the fix screen. This also covers the auto-connect variant, where a returning known phone would connect straight out of that screen via findLastKnownPhone.

Discovery restarts on onResume through recheckPermissions(), which already guards on isShowingBluetoothRequirement().

}

@Suppress("DEPRECATION")
private fun checkRequirementsAndStartDiscovery() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Code Review [follow-up]

Issue: The controller gained substantial new branching — prefersPhoneReader() gating, includeBluetooth propagation, the bluetoothUnavailable strip, enterNoReadersState, and the hasUsableReaders/pendingDiscoveryFailure discovery-failure gating — but WooPosCardReaderConnectionControllerTest was not extended to cover it. Only WooPosUnifiedDiscoveryStream received new tests, so the core decision logic of this PR is untested.

Suggestion: Add controller tests for the key paths: (1) a phone-preferring merchant with an unmet Bluetooth requirement stays in Scanning and discovery starts with includeBluetooth = false; (2) a discovery Failed event does not overwrite an already-found reader list (hasUsableReaders); and (3) phones dropping to zero re-derives the blocking / scanning-failed / scanning state via enterNoReadersState.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not taking all three. The branch names here are stale anyway, the code uses pendingBluetoothFailure and isShowingSomethingActionable().

Keeping controller coverage to the branch this PR is actually about, if anything: unmet Bluetooth requirement with no previously connected phone stays blocking and starts discovery with includeBluetooth = false.

@kidinov
kidinov marked this pull request as draft August 11, 2026 13:25
@kidinov
kidinov marked this pull request as ready for review August 11, 2026 13:43

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Code Review - Follow-up

  • Bluetooth-unavailable strip can bounce the user off the fix screen (onFixClicked doesn't cancel discoveryJob; handleReadersFound has no isShowingBluetoothRequirement() guard) [follow-up]: still-open - the only new commit renamed symbols; onFixClicked (WooPosCardReaderConnectionController.kt:299) still just sets the blocking state, and the size-1/else branches of handleReadersFound still overwrite it on the next phone snapshot.
  • WooPosCardReaderConnectionControllerTest not extended for the new branching [follow-up]: still-open - the latest push only renamed symbols (prefersPhoneReader -> hasConnectedPhoneReaderBefore, etc.); the phone-preferring / includeBluetooth / enterNoReadersState decision logic remains untested (only WooPosUnifiedDiscoveryStreamTest gained coverage).

New issues: 0

PR housekeeping
  • Applied repo POS guidance from AGENTS.md: the change stays within the POS card-reader connection controller/state/dialog/discovery stream and follows POS conventions (plain state machine, no store-app patterns).

Automatic review (follow-up) · claude-opus-4-8 · Workflow run

How to reply to a finding

Reply on this review (or inline at the line the finding refers to) with one of:

  • @claude addressed - I made the change. Bot verifies against the next diff before marking resolved.
  • @claude rejected: <reason> - Will not fix; reason gets quoted on the next review.
  • @claude not-applicable - Finding does not apply (wrong file, already covered elsewhere, etc.).

The bot honours these on the next review pass.

@samiuelson samiuelson self-assigned this Aug 13, 2026

@samiuelson samiuelson left a comment

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.

LGTM

@samiuelson
samiuelson merged commit c68955b into trunk Aug 14, 2026
16 checks passed
@samiuelson
samiuelson deleted the issue/WOOMOB-2784-skip-bt-gates-for-phone-reader branch August 14, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: point of sale POS project type: enhancement A request for an enhancement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants