[Android] Extract and expose ShopifyCheckout view - #456
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
| * connectivity. Its parent owns presentation state, geometry, and dismissal gestures. Call | ||
| * [destroy] when the view is permanently removed so the underlying WebView is released promptly. | ||
| */ | ||
| @SuppressLint("ViewConstructor") |
There was a problem hiding this comment.
ShopifyCheckout intentionally lacks Android’s standard XML-inflatable constructors:
- View(context)
- View(context, attrs)
- View(context, attrs, defStyleAttr)
ShopifyCheckout instead requires runtime state immediately:
ShopifyCheckout(context, checkoutUrl, listener, protocolClient)
Without the suppression, lint warns because subclasses of View are normally expected to support XML inflation.
The practical consequence is that this will not work:
<com.shopify.checkoutkit.ShopifyCheckout ... />
But direct construction and Compose’s AndroidView(factory = …) work normally. The annotation only silences that specific static-analysis warning; it changes no runtime behaviour.
| preloadCache.invalidate() | ||
| null | ||
| } | ||
| check(Looper.myLooper() == Looper.getMainLooper()) { |
There was a problem hiding this comment.
This is internal..
The responsibility is on the caller to run call on the main thread.
Rationale - the whole CheckoutView is UI construction. The whole thing should be on the UI thread, not just this internal WebView creation part.
Annotating the constructors for the View with @mainthread
Compose's AndroidView.factory already runs on the UI thread
| CheckoutWebView(activity as Context, webMessageTransport).apply { | ||
| loadCheckout(url) | ||
| } | ||
| return cachedView ?: run { |
There was a problem hiding this comment.
small refactor to avoid the non-null assert
| } | ||
| }, | ||
| modifier = Modifier.fillMaxSize(), | ||
| onRelease = CheckoutView::destroy, |
There was a problem hiding this comment.
Important and different from swift - consumers will be responsible for calling an explicit destroy() function
8febad5 to
fa38ddb
Compare
Package Size
Android file breakdown
Measured from the PR base SHA and PR head SHA. The file breakdown shows uncompressed sizes within each package artifact, so individual files do not sum to the compressed artifact total. This comment reports package artifact sizes only; it is not a final app binary-size report. |
fa38ddb to
253322d
Compare
2d9ed69 to
54efe2d
Compare
54efe2d to
565b063
Compare
| embeddedCheckoutProtocol.attach() | ||
| try { | ||
| embeddedCheckoutProtocol.attach() | ||
| } catch (error: UnsupportedWebViewException) { |
There was a problem hiding this comment.
adding this try-catch to catch the error, destroy the view.
We still re-throw the error so consumers get it
| * Initialization failures are reported on the main thread after this constructor returns. The | ||
| * returned view remains inert when initialization fails. | ||
| */ | ||
| @MainThread |
There was a problem hiding this comment.
@mainthread annotations are basically for Android Lint and IDE support to help folks be aware that they're calling from the wrong thread
565b063 to
288e313
Compare
288e313 to
0ca3772
Compare
Assisted-By: devx/7caad83a-1983-48f4-a173-2b35c37e1e4f
0ca3772 to
f616e04
Compare

What changes are you making?
Introduces a
ShopifyCheckoutview, a new publicFrameLayoutsubclass that lets host apps embed Shopify checkout inside their own presentation container (e.g. a Jetpack ComposeModalBottomSheet) rather than relying solely on the imperativeShopifyCheckoutKit.presentAPI.ShopifyCheckoutowns theThe host retains full control over sheet geometry, scrim, drag handle, snap points, and dismissal gestures. The view can be constructed directly for View-system and Java hosts, or created via the
ShopifyCheckout.createKotlin builder that accepts the sameCheckoutPresentationDSL used bypresent.Key behavioral details:
onCancelis invoked by the close button and system back (which navigates WebView history first when possible); the host is responsible for routing sheet-gesture dismissals through the same cancellation path.onFailasks the host to remove its presentation rather than attempting to dismiss an unknown parent.onFailis invoked and the view remains inert instead of crashing.destroy()is idempotent and must be called when the view is permanently removed.AndroidView.onReleaseis the recommended Compose integration point. The view also destroys itself when its nearestLifecycleOwneris destroyed.ShopifyCheckoutmust be created for a new checkout URL.Checkout chrome (toolbar, progress bar, loading background, close button) is extracted from
CheckoutBottomSheetinto a newcheckout_view_content.xmllayout owned byShopifyCheckout.CheckoutBottomSheetnow hosts aShopifyCheckoutinside a plainFrameLayoutcontainer, removing duplicated chrome logic.CheckoutWebView.checkoutViewForis refactored to run on the calling thread (must be main) and transport attachment now happens ina. try-catch in the constructor that destorys the partially constructed WebVeiw and rethrows on unsupported-feature failure.CheckoutBottomSheet.start()now returns aBooleanindicating whether presentation succeeded, allowingShopifyCheckoutKitto clean up its lifecycle observer on failure without relying on a caught exception.The demo app's cart screen is updated upstack to demonstrate (not in this PR)
How to test
CheckoutKitAndroidDemo).ModalBottomSheetviaShopifyCheckout.onCancelis invoked and the sheet closes.ShopifyCheckoutKit.presentflow (used elsewhere in the app) continues to work unchanged../gradlew :lib:test— newShopifyCheckoutTestand updatedInteropTestcover construction, preload consumption, lifecycle destruction, back navigation, cancel deduplication, and Java interop.Before you merge
Important
Important
platforms/swift/README.mdand/orplatforms/android/README.md)Releasing a new Swift version?
ShopifyCheckoutKit.podspecplatforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swiftplatforms/swift/README.md(major version only)Releasing a new Embedded Checkout Protocol version?
embeddedCheckoutProtocolAndroidinplatforms/android/gradle/libs.versions.tomlprotocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.apiif the public API changedReleasing a new Android version?
checkoutKitAndroidinplatforms/android/gradle/libs.versions.tomlplatforms/android/README.mdTip
Tip
See the Contributing documentation for the full release process per platform.