Repository navigation
feat(android): let products scan codes with the app's scanner #1319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
valentinfernandez1
merged 20 commits into
feat/host-scanner-core
from
feat/host-scanner-android
Oct 9, 2026
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
3c4bfd4
refactor(android): share the QR viewfinder and let the analyzer read …
valentinfernandez1 b16cce7
fix(android): close the contact picker when the product cancels
valentinfernandez1 d3f4b24
feat(android): draw the host scanner for products
valentinfernandez1 b5aaea3
fix(android): close host prompts robustly and read every code in a frame
valentinfernandez1 8b7d34d
fix(android): keep the scanner a sheet and close prompts only when on…
valentinfernandez1 2ca17c6
docs(android): explain the prompt lifecycle once
valentinfernandez1 4c9e709
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 0561423
fix(android): keep the screen behind the scanner hidden before the ca…
valentinfernandez1 902e4ae
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 3d6a002
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 63e0acc
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 09146d1
fix(android): answer NotVisible to a Worker scan while the app is in …
valentinfernandez1 f10cd66
fix(android): check a scan's visibility when its turn comes
valentinfernandez1 3a8d00e
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 56489e5
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 8b530b1
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 488e380
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 4cf90a3
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 b321475
fix(android): let a page scan again while the last scanner is closing
valentinfernandez1 55083f5
Merge branch 'feat/host-scanner-core' into feat/host-scanner-android
valentinfernandez1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
.../java/io/paritytech/polkadotapp/feature_products_impl/domain/truapi/ProductScanFormats.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package io.paritytech.polkadotapp.feature_products_impl.domain.truapi | ||
|
|
||
| import com.google.mlkit.vision.barcode.common.Barcode | ||
| import uniffi.truapi.CodeFormat | ||
|
|
||
| /** Translates between the formats a product names and the ones ML Kit reads. */ | ||
| internal object ProductScanFormats { | ||
| private val mlKitFormats = mapOf( | ||
| CodeFormat.QR to listOf(Barcode.FORMAT_QR_CODE), | ||
| CodeFormat.AZTEC to listOf(Barcode.FORMAT_AZTEC), | ||
| CodeFormat.DATA_MATRIX to listOf(Barcode.FORMAT_DATA_MATRIX), | ||
| CodeFormat.PDF417 to listOf(Barcode.FORMAT_PDF417), | ||
| CodeFormat.EAN13 to listOf(Barcode.FORMAT_EAN_13, Barcode.FORMAT_UPC_A), | ||
| CodeFormat.EAN8 to listOf(Barcode.FORMAT_EAN_8), | ||
| CodeFormat.UPC_E to listOf(Barcode.FORMAT_UPC_E), | ||
| CodeFormat.CODE128 to listOf(Barcode.FORMAT_CODE_128), | ||
| CodeFormat.CODE39 to listOf(Barcode.FORMAT_CODE_39), | ||
| CodeFormat.CODE93 to listOf(Barcode.FORMAT_CODE_93), | ||
| CodeFormat.ITF to listOf(Barcode.FORMAT_ITF), | ||
| CodeFormat.CODABAR to listOf(Barcode.FORMAT_CODABAR), | ||
| ) | ||
|
|
||
| fun mlKitFormats(formats: List<CodeFormat>): List<Int> = formats.flatMap(mlKitFormats::getValue) | ||
|
|
||
| /** | ||
| * The code as a product sees it, or null for a format no product can ask for. UPC-A is read | ||
| * as EAN-13 with a leading zero, which is how iOS reports it. | ||
| */ | ||
| fun codeFor(mlKitFormat: Int, text: String): Pair<CodeFormat, String>? = when (mlKitFormat) { | ||
| Barcode.FORMAT_UPC_A -> CodeFormat.EAN13 to "0$text" | ||
| else -> mlKitFormats.entries.firstOrNull { mlKitFormat in it.value }?.let { it.key to text } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
...n/java/io/paritytech/polkadotapp/feature_products_impl/domain/truapi/TrUAPIProductScan.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package io.paritytech.polkadotapp.feature_products_impl.domain.truapi | ||
|
|
||
| import androidx.lifecycle.Lifecycle | ||
| import io.parity.truapi.ScannerHostBridge | ||
| import io.paritytech.polkadotapp.common.presentation.resources.ContextManager | ||
| import io.paritytech.polkadotapp.common.utils.CoroutineDispatchers | ||
| import io.paritytech.polkadotapp.feature_products_impl.presentation.productBotManagement.ProductsRouter | ||
| import kotlinx.coroutines.delay | ||
| import kotlinx.coroutines.withContext | ||
| import kotlinx.coroutines.withTimeoutOrNull | ||
| import uniffi.truapi.HostScan | ||
| import uniffi.truapi.HostScannerScanRequest | ||
| import uniffi.truapi.ProductExecutionKind | ||
| import javax.inject.Inject | ||
| import javax.inject.Singleton | ||
| import kotlin.time.Duration.Companion.milliseconds | ||
| import kotlin.time.Duration.Companion.seconds | ||
|
|
||
| /** What the viewfinder shows: who is asking, and what they accept. */ | ||
| class ProductScanRequest( | ||
| val productId: String, | ||
| val executionKind: ProductExecutionKind, | ||
| val request: HostScannerScanRequest, | ||
| ) | ||
|
|
||
| /** | ||
| * Serves `scanner.scan`: opens the viewfinder for a product the user can see and waits for the | ||
| * code they scanned. A Worker has no page, so the app must be in front. The core already checked | ||
| * that its user tapped its card. | ||
| */ | ||
| @Singleton | ||
| class TrUAPIProductScans @Inject constructor( | ||
| private val productsRouter: ProductsRouter, | ||
| private val visibleProducts: VisibleProducts, | ||
| private val contextManager: ContextManager, | ||
| private val dispatchers: CoroutineDispatchers, | ||
| ) : TrUAPIPrompts<ProductScanRequest, HostScan>(unanswered = HostScan.Dismissed, notShown = HostScan.NotVisible), | ||
| ScannerHostBridge { | ||
| override suspend fun scanCode( | ||
| productId: String, | ||
| executionKind: ProductExecutionKind, | ||
| request: HostScannerScanRequest, | ||
| ): HostScan = ask(ProductScanRequest(productId, executionKind, request)) | ||
|
|
||
| override suspend fun canShow(question: ProductScanRequest): Boolean = withContext(dispatchers.main) { | ||
| when (question.executionKind) { | ||
| ProductExecutionKind.WORKER -> | ||
| contextManager.getActivity()?.lifecycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true | ||
| ProductExecutionKind.APP, ProductExecutionKind.WIDGET -> pageOnScreen(question.productId) | ||
| } | ||
| } | ||
|
|
||
| override suspend fun open() = productsRouter.openTrUAPIProductScan() | ||
|
|
||
| override suspend fun close() = productsRouter.closeTrUAPIProductScan() | ||
|
|
||
| // A host screen closing over the page, such as the last scanner, holds focus for a moment. | ||
| private suspend fun pageOnScreen(productId: String) = withTimeoutOrNull(FOCUS_RETURN_TIMEOUT) { | ||
| while (!visibleProducts.isOnScreen(productId)) delay(FOCUS_POLL_INTERVAL) | ||
| } != null | ||
|
|
||
| private companion object { | ||
| val FOCUS_RETURN_TIMEOUT = 1.seconds | ||
| val FOCUS_POLL_INTERVAL = 50.milliseconds | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.