Skip to content

[UNTIL-19417] Migrate ImageBitmap to Compose Multiplatform & add iOS sample app - #55

Merged
djordjeh merged 19 commits into
developfrom
shekar/task/UNTIL-19417/print-engine-kmp-3.0.5
Mar 26, 2026
Merged

[UNTIL-19417] Migrate ImageBitmap to Compose Multiplatform & add iOS sample app#55
djordjeh merged 19 commits into
developfrom
shekar/task/UNTIL-19417/print-engine-kmp-3.0.5

Conversation

@shekar-allam

@shekar-allam shekar-allam commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Migrate ImageBitmap from custom expect/actual class to Compose Multiplatform's ImageBitmap, enabling cross-platform image handling
  • Add iOS sample app with Compose Multiplatform support (Xcode project + SwiftUI bridge)
  • Implement iOS BarcodeEncoder using CoreImage CIFilter for QR code and Code 128 generation
  • Implement iOS encodeToBase64() using Skia bitmap APIs
  • Version bump to 3.0.5

Changes

  • print-engine: Replace expect class ImageBitmap with androidx.compose.ui.graphics.ImageBitmap, add api(compose.ui) dependency
  • print-plugins: Update all printer controllers to use asAndroidBitmap() bridge on Android
  • iosApp: New Xcode project with SwiftUI wrapper for Compose Multiplatform
  • sample/iosMain: iOS entry point MainViewController using ComposeUIViewController
  • Tests: Updated to use Bitmap.createBitmap().asImageBitmap() with Robolectric where needed
  • Bugfix: Fixed PrintCommand.Barcode.toString() returning "PrintCommand.Text" instead of "PrintCommand.Barcode"

Test plan

  • ./gradlew spotlessCheck passes
  • ./gradlew testDebug passes (all unit tests green)
  • iOS sample app builds and launches on simulator
  • Verify QR code / barcode generation on iOS device
  • Verify printing works on Android with physical printer

Output:

iOS

simulator_screenshot_00497344-CEE4-4BB1-BC84-C981C3A8DAD0

Android

image

🤖 Generated with Claude Code

Replace custom expect/actual ImageBitmap with androidx.compose.ui.graphics.ImageBitmap
from Compose Multiplatform as the unified cross-platform image abstraction.

- Use Compose ImageBitmap directly across all modules (no typealias indirection)
- Implement iOS encodeToBase64() via Skia PNG encoding
- Implement iOS BarcodeEncoder using CoreImage (CIQRCodeGenerator, CICode128BarcodeGenerator)
- Convert Android plugin controllers (Star, Epson, Sunmi) to use asAndroidBitmap() at SDK boundary
- Android BarcodeEncoder returns Compose ImageBitmap via asImageBitmap()
- Add Compose plugins to print-engine, star, and epson modules
- Update all test files for new ImageBitmap type conversions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@shekar-allam
shekar-allam requested a review from a team as a code owner March 18, 2026 11:59
@shekar-allam
shekar-allam changed the base branch from master to develop March 18, 2026 12:03
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@shekar-allam
shekar-allam force-pushed the shekar/task/UNTIL-19417/print-engine-kmp-3.0.5 branch from 7e68853 to 8cebb40 Compare March 18, 2026 12:34
@shekar-allam
shekar-allam requested a review from djordjeh March 18, 2026 12:36
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread print-engine/build.gradle.kts Outdated
shekar-allam and others added 8 commits March 19, 2026 14:17
…OS printing

- Remove INFOPLIST_KEY_UIApplicationSceneManifest_Generation from project.pbxproj
  to prevent auto-enabling UIApplicationSupportsMultipleScenes=true, which caused
  Compose to render into zero-size frames on iPad (Stage Manager / multi-window)
- Add UIApplicationSceneManifest{UIApplicationSupportsMultipleScenes=false} and
  NSBluetoothAlwaysUsageDescription to Info.plist; without the Bluetooth key iOS
  silently terminates any app linked against CoreBluetooth on physical devices
- Wrap Epos2Discovery.start() in withContext(Dispatchers.Main) — Epson SDK requires
  the iOS main thread for discovery start/stop
- Implement EpsonPrinterController, EpsonPrintService, EpsonServiceProvider,
  StarServiceProvider, EpsonPrinterErrorState iOS actuals (replace TODO stubs)
- Add nativeInterop cinterop def + README for Epson ePOS2 SDK integration
- Wire up MainViewController to live printer discovery and print job dispatch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…thread safety

- Fix all ePOS2.h enum constants to match actual SDK header values:
  EPOS2_MECHANICAL_ERR 0→1, AUTOCUTTER_ERR 1→2, UNRECOVER_ERR 2→3,
  AUTORECOVER_ERR 3→4, CODE_PRINTING 1→13, LOW_VOLTAGE_ERR 0→1
- Add EPOS2_NO_ERR and EPOS2_UNKNOWN checks in evaluateStatusInfo
- Check every Epson SDK return code via checkSdkResult() — iOS SDK
  returns int error codes unlike Android which throws Epos2Exception
- Guard disconnect() — only call if connect() succeeded
- Close SkiaBitmap/SkiaImage after use to prevent native memory leaks
- Extract shared ImageBitmap.encodeToPngBytes() utility
- Replace remember{scope.launch} with LaunchedEffect(Unit)
- Use flowOn(IO) + collect on Main to avoid mutating Compose state
  from background dispatcher
- Replace leaked module-level CoroutineScope with rememberCoroutineScope()
- Guard startPrintJob behind initialized check
- Fall back to TM-T88 for unknown printer models instead of crashing
- Close SkiaImage in BarcodeEncoderImpl after toComposeImageBitmap()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ocol

- Show discovery state (searching/found/error) in the sample UI so
  connection issues are immediately visible on device
- Add UISupportedExternalAccessoryProtocols with com.epson.escpos to
  Info.plist for MFi Bluetooth/USB printer discovery
- Fix initialized flag to use mutableStateOf so it survives recomposition

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Strip all printer discovery code from this branch to keep it focused
on core iOS printing infrastructure. Discovery will be added as a
separate feature in the UNTIL-19514 branch.

Removed:
- EpsonPrinterDiscovery.ios.kt implementation (reverted to TODO stub)
- Discovery flow collection and discoveryStatus from MainViewController
- discoveryStatus parameter from PrinterLayout
- Discovery permissions from Info.plist (Bluetooth, LocalNetwork,
  Bonjour, ExternalAccessory)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement full Epson printer discovery on iOS using the ePOS2 SDK:

- EpsonPrinterDiscovery.ios.kt: callbackFlow-based discovery using
  Epos2Discovery with Epos2FilterOption (all ports, printer type).
  Dispatches start() to Main thread, references delegate in awaitClose
  to prevent GC, maps device info to ExternalPrinter model
- MainViewController: LaunchedEffect-based discovery flow collection
  with discoveryStatus state (Idle/Discovering/Error), flowOn(IO)
  for background work with Main-thread Compose state updates
- PrinterLayout: discoveryStatus parameter showing search progress
- Info.plist: NSBluetoothAlwaysUsageDescription, NSLocalNetworkUsageDescription,
  NSBonjourServices, UISupportedExternalAccessoryProtocols for
  Bluetooth/LAN/USB printer discovery permissions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Gradle task to auto-download Epson ePOS SDK xcframework on first
  iOS build, eliminating manual setup steps
- Consolidate SDK config into EpsonSdk object with centralized version,
  repo URL, and architecture slice constants
- DRY up iOS target configuration with configureEpsonInterop() extension
- Move EpsonServiceProviderTest from instrumented to unit test with
  proper null-guard coverage
- Update nativeInterop README with auto-download instructions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ekar/task/UNTIL-19514/print-engine-epson-ios-support
- Add Gradle task to auto-download Epson ePOS SDK xcframework on first
  iOS build, eliminating manual setup steps
- Consolidate SDK config into EpsonSdk object with centralized version,
  repo URL, and architecture slice constants
- DRY up iOS target configuration with configureEpsonInterop() extension
- Move EpsonServiceProviderTest from instrumented to unit test with
  proper null-guard coverage
- Update nativeInterop README with auto-download instructions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@shekar-allam
shekar-allam force-pushed the shekar/task/UNTIL-19417/print-engine-kmp-3.0.5 branch from b99ae89 to db1b743 Compare March 20, 2026 08:41
shekar-allam and others added 3 commits March 20, 2026 10:02
…ekar/task/UNTIL-19514/print-engine-epson-ios-support

# Conflicts:
#	print-plugins/epson/build.gradle.kts
#	print-plugins/epson/src/androidUnitTest/kotlin/de/tillhub/printengine/epson/EpsonServiceProviderTest.kt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nts directly

Enable cinterop commonization so Epson SDK constants (enums, #defines) are
available in the shared iosMain source set. Replace ~60 hardcoded constants
with direct imports from com.epson.epos2.

Add a UIKit shim header that imports only UIImage.h instead of all of UIKit,
preventing the commonizer from crashing on platform-specific UIKit types
(UIMenuSystemElementGroupPreference) that differ across iOS targets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
shekar-allam and others added 3 commits March 24, 2026 12:44
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ral only

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
shekar-allam and others added 2 commits March 24, 2026 13:04
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ine-epson-ios-support

[UNTIL-19514] Print engine epson ios support
@djordjeh djordjeh self-assigned this Mar 25, 2026
@djordjeh
djordjeh merged commit 9fdb03d into develop Mar 26, 2026
1 check passed
@djordjeh
djordjeh deleted the shekar/task/UNTIL-19417/print-engine-kmp-3.0.5 branch March 26, 2026 14:12
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.

3 participants