diff --git a/README.md b/README.md index 9c49a0d6..ae610947 100644 --- a/README.md +++ b/README.md @@ -3,241 +3,45 @@ This library makes the integration of all barcode scanners easy in any Android application, avoiding vendor lock-in and lowering the cost of advanced scanner integration. -It is compatible with: -* Zebra EMDK devices (which comprise most of their integrated systems like the TC25, TC75, WT6000...) -* Zebra Bluetooth scanners -* Honeywell AIDC integrated devices (including CN* devices) -* Athesi SPA43 -* GeneralScan Bluetooth rings -* And a few others, check full compatibility table below. +It is compatible with a wide variety of scanning devices, integrated or external, from different +vendors such as Zebra, Honeywell, Athesi and more. -When there are no compatible hardware devices available, the library provides a camera reader based on ZBar (default) or ZXing. +When there are no compatible hardware devices available, the library provides a camera reader based +on [ZBar](https://mvnrepository.com/artifact/me.dm7.barcodescanner/zbar) (default) or +[ZXing](https://mvnrepository.com/artifact/com.google.zxing/core). -Through a common abstraction, it provides access to the following methods (provided the hardware supports them): -* press/release the scanner's trigger -* pause/resume scanning abilities -* disconnect/reconnect scanners -* enable/disable illumination from the scanner -* enable/disable colored LEDs -* set scanner enabled symbologies +Through a common abstraction, it provides access to the following methods (provided the hardware +supports them): +- press/release the scanner's trigger +- pause/resume scanning abilities +- disconnect/reconnect scanners +- enable/disable illumination from the scanner +- enable/disable colored LEDs +- set scanner enabled symbologies -Finally, it provides a ready to use Service that handles scanner lifecycles, as well as a template Activity, and a sample demo -application, allowing to use scanners in a matter of minutes. +Finally, it provides a ready to use Service that handles scanner lifecycles, as well as a template +Activity, and a demo application, allowing you to use scanners in a matter of minutes. -# Compatibility matrix - -The different plugins do not all have the same capabilities. The following table sums up what is possible to do with this library on common device families. - -* "?" means the library does not support the function, but the underlying SDK or device may support it and the capability may be added later. -* "N/A" means this would make no sense on this device. -* "All 1D" symbologies means code 128, 39, I25, D25, EAN13. (some devices also provide QR code and other 2D symbologies) -* "Plugin AAR needed" means an additional aar must be used for this device. This is likely because it contains non-OSS code and cannot be distributed freely. -* "SDK needed" means the library will need the manufacturer SDK to work. This SDK likely could not be included for licence reasons, and must be provided by valid SDK license holder. -* "Provider name" is a name internal to the library which can be used to enable or disable a provider (by default all providers present are used which can slow initialisation). - -Manufacturer | Device family | Plugin AAR needed | SDK needed | Provider name | Connection | Notes | Basic scanning | Symbologies recognized | Symbologies detection | Symbology configuration | Illumination control | Disable trigger | LED control | Beep control | Trigger control ------------- | ---------------------- | ----------------- | ---------- | ------------------------------------------- | ---------- | ------------------------------ | -------------- | --------------------------- | --------------------- | ----------------------- | -------------------- | --------------- | ----------- | ------------ | --------------- -Zebra | Integrated: TC25... | Yes | No | EmdkProvider | Integrated | EMDK devices | Yes | All 1D | Yes | On startup | No | Yes | N/A | No? | ? -Zebra | BT ring RS6000,5100 | Yes | Yes | BtZebraProvider | BT SPP | | Yes | All 1D | Yes | On startup | No | Yes | Yes | Yes | ? -Zebra | BT ring RS6000,5100 | No | No | ZebraOssSppScannerProvider | BT SPP | Alternate pure OSS provider | Yes | All 1D | Yes | On startup | No | Yes | Yes | Yes | Yes -Bluebird | Integrated: EF500... | No | No | BluebirdProvider | Integrated | | Yes | Most, save D25 | Yes | ? | No | Yes | N/A | ? | ? -Athesi | Integrated: SPA43LTE | No | No | AthesiHHTProvider | Integrated | Device must be named SPA43LTE | Yes | All 1D | Yes | On startup | No | Yes | N/A | No? | Yes -Athesi | Integrated: SPA43LTE | No | No | AthesiE5LProvider | Integrated | Device must be named RD50TE | Yes | All 1D | No | No? | No | No | N/A | No? | Yes -Honeywell | Integrated: EDA50... | Yes | Yes | AIDCProvider | Integrated | AIDC devices (Intermec) | Yes | All 1D | Yes | On startup | No | ? | N/A | No? | ? -Honeywell | Integrated: EDA52 | No | No | HoneywellOssIntegratedScannerProvider | Integrated | Device must be named EDA52 | Yes | All 1D | Yes | No | No | ? | N/A | No? | Yes -Honeywell | BT Voyager 1602g | No | No | HoneywellOssSppScannerProvider | BT SPP | | Yes | All 1D | Yes | No | No | ? | N/A | No? | Yes -GeneralScan | BT ring R5000BT | No | No | GsSppScannerProvider | BT SPP | | Yes | All 1D | No | ? | No | No | No | No | ? -ProGlove | BT glove Mark II | No | No | ProgloveProvider | BT BLE | ProGlove app needed | Yes | All 1D | Yes | ? | No | No | Yes | No? | ? -Koamtac | BT KDC (180...) | Yes | No | Koamtac | BT BLE | Device must be named KDC* | Yes | All 1D | Yes | ? | No | Yes | Yes | Yes | ? -M3 | RingScanners | Yes | No | M3RingScannerProvider | BT SPP | M3 app needed | Yes | All 1D | No | No | No | Yes | No | No | ? -Camera | Devices with camera | No | No | | Integrated | Capabilities depend on device | Yes | All 1D | Yes | On startup | Yes (flash light) | Yes | N/A | Yes | N/A - -There also are traces of Postech BT ring scanner compatibility - their communication protocol is like the GeneralScan one, but the authors of the library lacked a test device to finish it, so it was disabled. -Note that many scanners not mentioned here are interpreted by the OS as a keyboard (HID devices), and as such are not supported in a specific way by the library. As for the camera, it is initialized in a different way than other scanners, we recommend taking a look at the template activity to understand its usage. - -# Adding the library to an Android application - -For all devices which are not marked as "plugin AAR needed" in the above table, a single dependency is needed. It is available either here on Github or (better) or Maven Central: just use coordinates `com.enioka.scanner:scanner:x.y.z:aar` (do remember to specify `mavenCentral()` inside the Gradle build file `repositories` section), and update the version to latets version). - -That's all if you do not need an AAR plugin. If you also need a plugin AAR (provided by us) and a proprietary SDK (provided by the scanner manufacturer, at the root of your app: (all files *are inside the source tree or created by the project*) -* copy barcode_scanner_library_*.aar (the Zebra Bluetooth SDK) to aar_zebra -* copy DataCollection.jar (Honeywell SDK) to barcodelibs - -Inside aar_zebra: +In order to use **enioka Scan**, you need to add the corresponding dependency to your `build.gradle`. ```groovy -configurations.maybeCreate("default") -artifacts.add("default", file('barcode_scanner_library_v2.0.8.0.aar')) -``` - -Inside settings.gradle, add 'aar_zebra" to the list of includes. - -Inside the application build.gradle, add: - -```groovy -dependencies { - compile project(':aar_zebra') - - compile 'me.dm7.barcodescanner:zbar:1.8.3' - - compile 'com.android.support:appcompat-v7:28.0.0' - compile 'com.android.support.constraint:constraint-layout:1.0.2' +repositories { + mavenCentral() } -``` - -Inside the manifest of your application, nothing to do. - -# Using the library - -There are different ways to use the library, depending on where it has to be used. And in any ways, the library does not hide its low-level objects which are an always available fallback. -Most scanner capabilities are exposed through the [`Scanner`][scanner-api] API, though the default behavior of the [`ScannerServiceApi`][scanner-service-api] API ought to be enough for simply receiving scanned data. - -## In an Activity - -This is the most usual use of for the library. The library provides the class [`ScannerCompatActivity`][scanner-compat-activity] from which to inherit. The most simple activity ever possible to use a scanner is this: - -```java -import com.enioka.scanner.activities.ScannerCompatActivity; - -public class MyScanningActivity extends ScannerCompatActivity { -} -``` - -This creates an activity with a very simple layout which display status messages from the scanners as well as scanning results and a button to toggle illumination. Now, most activities of course want to use their own layouts. This is allowed by setting two fields (one for the camera layout, one for the laser layout) in the constructor of the activity: - -```java -@Override -protected void MyConstructor() { - super.onCreate(); - layoutIdLaser = R.layout.activity_parcel_scan_laser; - layoutIdCamera = R.layout.activity_parcel_scan_camera; -} -``` - -There are a few other fields to allow you to customize further the activity, which are all documented inside the class. These include options to rename the toggle illumination button ID, to disable camera or laser or both, or enable a fallback dialog with manual input and auto-completion. - -If the provided template does not suit your needs, but you still want to take advantage of the [`ScannerServiceApi`][scanner-service-api], simply make sure that your activity follows the steps described in the section below. - -if you use a custom layout, you need to use our Camera scanning view. The ID of this view should by default be `camera_scan_view` (which can be customized inside the constructor as for the other views). This view accepts a few parameters, which are displayed here with their default values: - -```xml - -``` - -Finally, note that inside the activity code there are a few hooks that can be overloaded - these are public or protected methods, with full javadoc. Of notice are:* -* `onData(List data)` -* `onStatusChanged(Scanner, ScannerStatusCallback.Status)` - -## Outside an activity - -The library exposes a service which can be bound as any other bound service: - -```java -ScannerServiceApi scannerService; -boolean serviceBound = false; - -ServiceConnection connection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - // We've bound to ScannerService, cast the IBinder and get the ScannerServiceApi instance - ScannerService.LocalBinder binder = (ScannerService.LocalBinder) service; - scannerService = binder.getService(); - serviceBound = true; - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - serviceBound = false; - scannerService = null; - } - }; - -// Bind to ScannerService service -// Make sure to bind the ScannerService class (or any implementation of the API), not the ScannerServiceApi interface -Intent intent = new Intent(this, ScannerService.class); -bindService(intent, connection, Context.BIND_AUTO_CREATE); -``` - -It is then possible to use the [`ScannerServiceApi`][scanner-service-api] object to access the different endpoints of the service. The most interesting one is `registerClient()` which hooks scanning callbacks and scanner/provider discovery notifications to a class implementing the [`ScannerClient`][scanner-client] interface such as a custom activity. - -Please remember to unbind the service when it is not needed anymore, as for any other service. This will often be in "onDestroy" hooks. Also, as this is a bound service, it is destroyed whenever it has no bound clients left. Many applications actually bind the service on startup onto the application context to be sure it is never destroyed and therefore is very quick to bind from anywhere, but this depends on the use-case and is not compulsory at all. - -If you want to bind the service inside your application class, a helper is provided to avoid boilerplate code that can be used as such: - -```java -public class App extends Application { - ScannerServiceBinderHelper serviceBinder; - - @Override - public void onCreate() { - super.onCreate(); - serviceBinder = ScannerServiceBinderHelper.bind(this); // a second overload exists with a configuration Bundle. - } - - @Override - public void onTerminate() { - super.onTerminate(); - serviceBinder.disconnect(); - } +dependencies { + implementation 'com.enioka.scanner:scanner:2.4.1:aar' } ``` -Finally, there are a few Intent extra properties which can be set to control the behaviour of the service such as filters used in the scanner search. -These can be found as static strings inside the [`ScannerServiceApi`][scanner-service-api] interface, and methods in the [`ScannerSearchOptions`][scanner-search-options] class help converting search parameters to and from those intent extras. - -## Using the camera - -When there is no laser scanner available, or when a button is clicked, the `ScannerCompatActivity` activity will fallback to using the device camera (if any) to scan barcodes. This leverages two different barcode scanning libraries, ZBar and ZXing in recent versions. It is compatible both with very old Camera APIs as well as more recent Camera 2 APIs. It tries to set the best camera parameters for barcode scanning, including dynamically setting the camera resolution according to the processing speed of the device. As a result, it may take a few dozen seconds to reach the most suitable settings on first startup - the resolution is then stored and reused on subsequent initialisations. - -The camera scanner can actually be used easily inside your own activities without any links with the rest of the library (no need to use the scanner service, etc) by just adding the `CameraBarcodeScanView` to your layouts, and then registering a callback on this view using `CameraBarcodeScanView.setResultHandler`. Other APIs are available to enable the torch or pause scanning. - -The provided Camera activity will display a target rectangle, which can be moved or tapped to change or refresh the autofocus area. - -# Developer quick start (modifying this library) - -## Developing for Android - -In order to start developing and testing the library: -* Have Android Studio installed and open the project with it -* Connect a compatible android device via USB, it should then show up in Android Studio's device manager -* Run the app with the android device selected - -In case the android device is not detected by Android Studio: -* Make sure the device is in developer mode and has USB Debugging enabled -* Make sure the USB cable supports data transfer (some cables only support charging) - -## Adding another SDK to the library - -A scanner SDK contains at least a [`Scanner`][scanner-api] implementation (interfacing between the code and the device) and a [`ScannerProvider`][scanner-provider-api] implementation (handling scanner creation and whether the SDK is compatible with the search). - -In order for a new scanner SDK to be found by the library, the [`ScannerProvider`][scanner-provider-api] implementation needs to be declared as a service in its `AndroidManifest.xml` with an intent-filter containing the action `com.enioka.scan.PROVIDE_SCANNER`. -The associated Java class does not need to extend Android's `Service` class (the `tools:ignore="Instantiatable"` attribute may be added to the service in the manifest), but it must provide a public default constructor as it will be instantiated using `Class.getName()`. - -See the [Mock SDK][mock-sdk] for an example of addon SDK. +More artefacts may be required depending on your devices, the full list and compatibility matrix are +available in the documentation. -# Release process +## Documentation -To publish the library to Maven Central and GitHub releases, a tag must be created and attached to the appropriate commit. -The tag will trigger a workflow that will automatically create a github release containing the AAR file, and publish the library to Maven Central. -If the release is created manually, the workflow will not run and the library will not be published correctly. Only the tag needs to be manually created. +You can learn more about **enioka Scan** by reading the [official documentation](https://enioka-scan.readthedocs.io/en/latest/). -[mock-sdk]: ./enioka_scan_mock -[scanner-api]: ./enioka_scan/src/main/java/com/enioka/scanner/api/Scanner.java -[scanner-client]: ./enioka_scan/src/main/java/com/enioka/scanner/service/ScannerClient.java -[scanner-compat-activity]: ./enioka_scan/src/main/java/com/enioka/scanner/activities/ScannerCompatActivity.java -[scanner-provider-api]: ./enioka_scan/src/main/java/com/enioka/scanner/api/ScannerProvider.java -[scanner-search-options]: ./enioka_scan/src/main/java/com/enioka/scanner/api/ScannerSearchOptions.java -[scanner-service-api]: ./enioka_scan/src/main/java/com/enioka/scanner/service/ScannerServiceApi.java \ No newline at end of file +Most notably: +- [The quick-start guide](https://enioka-scan.readthedocs.io/en/latest/quickstart.html) +- [Dependencies and compatible devices](https://enioka-scan.readthedocs.io/en/latest/dependencies.html) +- [The API reference](https://enioka-scan.readthedocs.io/en/latest/api/index.html) +- [How to develop for enioka Scan](https://enioka-scan.readthedocs.io/en/latest/develop.html) \ No newline at end of file diff --git a/docs/api/camera.md b/docs/api/camera.md new file mode 100644 index 00000000..0a55de9b --- /dev/null +++ b/docs/api/camera.md @@ -0,0 +1,167 @@ +# The camera scanner + +**enioka Scan** can still be used to manage barcodes scanned using the Android camera. For that, it +exposes the `CameraBarcodeScanView` and a series of helper classes to handle compatibility with both +Camera 1 and Camera 2 hardware APIs. + +This page regroups all information needed to control this special scanner at a deeper level. + +## The `CameraBarcodeScanView` class + +This is the base Android `FrameLayout` used by the activity to display the camera. This view wraps +the API-specific `CameraBarcodeScanViewBase` implementation and handles the API selection based on +the device's properties. + +For most users, this is the class they will interact with if they need to interact with the Camera +hardware and not just the [`Scanner` instance](scanner.md#the-scanner-interface). + +:::{method} setReaderMode(CameraReader readerMode) -> void + +Change the library used to read barcodes from the camera feed. + +:param CameraReader readerMode: The library to use, between `CameraReader.ZBAR` and + `CameraReader.ZXing` +::: + +:::{method} addSymbology(BarcodeType barcodeType) -> void + +Add a symbology to detect. By default, only `CODE_128` is used. + +:param BarcodeType barcodeType: The symbology to add. +::: + +:::{method} setResultHandler(ResultHandler handler) -> void + +Change the callback used when a barcode is read. + +Used by `CameraBarcodeScanViewScanner` to register itself with the view and correctly propagate +barcodes read by the camera as any regular scanner reads. + +:param ResultHandler handler: the + [`ResultHandler` implementation](scanner_callbacks.md#the-camerabarcodescanviewresulthandler-interface) +::: + +:::{method} setTorch(boolean value) -> void + +Switch the camera's torch on or off. + +:param boolean value: Indicate if the torch should be turned on (true) or off (false) +::: + +:::{method} getSupportTorch() -> boolean + +:returns: true if the camera supports torch activation. false otherwise. +::: + +:::{method} getTorchOn() -> boolean + +:returns: true if the camera's torch is on, false otherwise. +::: + +:::{method} cleanUp() -> void + +Unhooks all camera callbacks and closes it. After this method is called, the camera becomes unusable +and the view needs to be reinitialized. +::: + +:::{method} pauseCamera() -> void + +Pauses the camera's capture. +::: + +:::{method} resumeCamera() -> void + +Resumes the camera's capture. +::: + +:::{method} getLatestSuccessfulScanJpeg() -> byte[] + +:returns: The JPEG data of the image used in the latest successful scan, or null if there is no + previous scan data. +::: + +## Use `CameraBarcodeScanView` in your custom layout + +To use this view in your own layout, you can add the following block to its XML definition and adapt +the attributes to your needs: + +```xml + +``` + +The main XML attributes are as follow: + +:::{method} app:forceCameraApiVersion + +"1" for the old camera API, "2" for the Camera2 API, any other value to let the library detect the +appropriate camera API for this device. +::: + +:::{method} app:useAdaptiveResolution + +If true, the resolution of the camera preview may be decreased if performances are deemed too low +by the frame analyzer. +::: + +:::{method} app:minResolutionY + +The minimum vertical resolution of the camera preview, after which resolution can no longer be +decreased by adaptive resolution when trying to improve performance. +::: + +:::{method} app:maxResolutionY + +The maximum vertical resolution of the camera preview, useful to limit performance costs. +::: + +:::{method} app:readerMode + +"1" for ZXING, any other value for ZBAR. +::: + +:::{method} app:storePreferredResolution + +If true, the app will persists the most used preview resolution to the application's preference. +::: + +:::{method} app:targetColorActive + +The default color of the "target" indicating where barcodes are expected to be. +::: + +:::{method} app:targetColorPaused + +The color of the "target" indicating where barcodes are expected to be, used whenever the scanning +is paused. +::: + +:::{method} app:targetIsFixed + +If true, the target cannot be moved by the user. If false, the user can drag the target up and down +on the preview. +::: + +:::{method} app:targetStrokeWidth + +The thickness of the target's lines. +::: \ No newline at end of file diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 00000000..55562f4f --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,32 @@ +# API reference + +% Notes: + +% Due to the limitations of Sphinx and MyST, documentation formatting was 'hacked' a bit by using +% python and cpp directives as Java is not natively supported and has no proper third-party option. + +% API guides for the BluetoothScanner API may be helpful for provider developers but is not a +% priority. A full bluetooth guide may be needed, as well as a detailed overview of the library's +% workflow from service startup to scanner availability. + +This section contains more in-depth info about important classes and interfaces of **enioka Scan**. + +:::{toctree} +:caption: For most users +:maxdepth: 2 + +scanner +scanner_service +scanner_callbacks +scanner_activity +camera +others +::: + +:::{toctree} +:caption: For advanced users and enioka Scan developpers +:maxdepth: 2 + +lazerscanner +scanner_provider +::: \ No newline at end of file diff --git a/docs/api/lazerscanner.md b/docs/api/lazerscanner.md new file mode 100644 index 00000000..b9a54f36 --- /dev/null +++ b/docs/api/lazerscanner.md @@ -0,0 +1,40 @@ +# The LazerScanner class + +`LazerScanner` is the main scanner factory and the core of the library. It is in charge of finding +scanner providers and scanner devices. Most users will let the +[`ScannerService`](scanner_service.md#the-scannerserviceapi-interface) +handle its own instance of `LazerScanner`, but should you need it, you can use its static methods +directly. + +:::{method} discoverProviders(Context ctx, ProviderDiscoveredCallback cb) -> void + +Discovers scanner providers through service intent and retrieves them through reflection. +The providers are cached and do not need to be discovered again unless new entries are expected, it +is a costly operation. + +:param Context ctx: a context used to retrieve a `PackageManager` +:param ProviderDiscoveredCallback cb: The callback used whenever a provider is found. + Automatically wrapped by a `ProviderDiscoveredCallbackProxy`, for which a public overload exists +::: + +:::{method} getProviderCache() -> List + +:returns: the list of provider keys from the current provider cache, including bluetooth ones if + available (the cache needs to have been initialized first). +::: + +:::{method} getLaserScanner(Context ctx, ScannerConnectionHandler handler, ScannerSearchOptions options) -> void + +Search for new laser scanners with available providers. The scanner is provided through a callback. +There is a specific callback when no scanner is available. + +:param Context ctx: a context used to retrieve a `PackageManager` +:param ScannerConnectionHandler handler: The callback used whenever a scanner is found. + Automatically wrapped by a `ScannerConnectionHandlerProxy`, for which a public overload exists. +:param ScannerSearchOptions options: The options used to refine the search. +::: + +:::{seealso} + +* The [Callbacks](scanner_callbacks.md) documentation +::: \ No newline at end of file diff --git a/docs/api/others.md b/docs/api/others.md new file mode 100644 index 00000000..43bd2d6b --- /dev/null +++ b/docs/api/others.md @@ -0,0 +1,24 @@ +# Other useful classes + +The library contains a lot of other smaller classes you may need to interact with. This page will +regroup most of them. + +## The `BarcodeType` enum + +This enum contains every barcode symbology officially supported by **enioka Scan**. Whenever a +scanner reads data, it will try to match its symbology with one of these. This list is not final and +will likely be extended in the future to include other symbologies. This is also the enum to use +when configuring a scanner. + +:::{cpp:enum} BarcodeType + +:CODE128: The Code 128 symbology +:CODE39: The Code 39 symbology +:DIS25: The Discrete-2-of-5 symbology +:INT25: The Interleaved-2-Of-5 symbology +:EAN13: The EAN 13 symbology +:QRCODE: The regular QR Code symbology +:AZTEC: The Aztec 2D symbology +:UNKNOWN: Any other symbology not currently part of the enum. When used during symbology + configuration, means the scanner should allow all symbologies. +::: diff --git a/docs/api/scanner.md b/docs/api/scanner.md new file mode 100644 index 00000000..3c4dd052 --- /dev/null +++ b/docs/api/scanner.md @@ -0,0 +1,289 @@ +# The Scanner API + +The `Scanner` interface is the central part of the library. It is the piece of code that gives the +user control on the physical scanner devices. All devices have common methods relating to basic +functionalities (reading a barcode) or lifecycle control. Some devices have access to extra features +which are represented here with optional interfaces. + +All methods accepting a `ScannerCallback` type as a parameter have a default implementation to +wrap these callbacks in a Proxy Callbacks to ensure proper jumps to the UI thread. You may also +directly call the overloaded method with a proxy callback. + +:::{seealso} + +* The [Callbacks](scanner_callbacks.md) documentation. +::: + +## The `Scanner` interface + +These methods are guaranteed to be supported by all scanning devices. + +:::{cpp:enum} Mode + +This enum represents the scanning mode the device will be set to. + +:SINGLE_SCAN: The scanner stops after one successful read. It must be rearmed. +:CONTINUOUS_SCAN: The scanner waits for the result post-treatment and automatically rearms. +:BATCH: The scanner is always ready to scan, not waiting for any result analysis. Results may be + sent in batches. +::: + +:::{method} initialize(...) -> void + +The scanner initializer, called once per application launch. It handles the creation of links +between the library and the physical devices and registers callbacks. + +:param Context applicationContext: The android context of the application +:param ScannerInitCallback initCallback: The callback called after the initialization is complete +:param ScannerDataCallback dataCallback: The callback called after data is read +:param ScannerStatusCallback statusCallback: The callback called after a status change +:param Mode mode: The scanning mode +:param Set symbologySelection: Which barcode symbologies the scanning device should be + set to recognize. +::: + +:::{method} getProviderKey() -> String + +For logging and sorting purpose, this is the key of the SDK behind this scanner (same as +[`ScannerProvider.getKey()`](scanner_provider.md#the-scannerprovider-interface)) +::: + +:::{method} setDataCallBack(ScannerDataCallbackProxy cb) -> void + +Changes the scanner's data callback. There is no "non-proxy" overload of this method. + +:param ScannerDataCallbackProxy cb: The new callback, called when data is read +::: + +:::{method} disconnect(@Nullable ScannerCommandCallback cb) -> void + +Disconnect scanner from the App (the app does not need the scanner anymore). + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null + or absent. +::: + +:::{method} pause(@Nullable ScannerCommandCallback cb) -> void + +The app keeps the scanner for itself but does not need it immediately. It may free whatever +resources it has, or ignore this call. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +:::{method} resume(@Nullable ScannerCommandCallback cb) -> void + +Reverse the effects of `pause()`. The scanner is once again ready to scan after +this call. The scanner's status callback should be called if needed. Idempotent. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +## Extra functionality support + +Some scanners have support for extra features, but it may not be supported by all devices. As such, +these "extra" functionalities are considered supported on a per-SDK level and represented by +interfaces a Scanner may or may not implement (on top of `Scanner`). + +To check if a scanner has access to such functionalities, simply call the `getSupport()` +method. If the feature is supported by the device, the function returns the scanner instance casted +to the corresponding interface. If the feature is not supported, the method returns null. + +For example, if you wanted to programmatically activate the trigger of every scanning device +connected to the `ScannerService`, you would write the following code: + +```java +for (final Scanner s : scannerService.getConnectedScanners()) { + final WithTriggerSupport scannerTriggers = s.getTriggerSupport(); + if (scannerTriggers != null) { + // This scanner has support for trigger activation, you may continue + scannerTriggers.pressScanTrigger(); + } else { + // This scanner does not support this functionality + continue; + } +} +``` + +### The `WithBeepSupport` interface + +:::{method} Scanner.getBeepSupport() -> WithBeepSupport +::: + +Extra interface implemented by scanners that support beeps. + +It exposes the following methods: + +:::{method} beepScanSuccessful(@Nullable ScannerCommandCallback cb) -> void + +Short high beep to indicate successful scan + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +:::{method} beepScanFailure(@Nullable ScannerCommandCallback cb) -> void + +Long low beep to indicate unsuccessful scan + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +:::{method} beepPairingCompleted(@Nullable ScannerCommandCallback cb) -> void + +Different beep to indicate a completed barcode pairing + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +### The `WithTriggerSupport` interface + +:::{method} Scanner.getTriggerSupport() -> WithTriggerSupport +::: + +Extra interface implemented by scanners that support software triggers. + +It exposes the following methods: + +:::{method} pressScanTrigger(@Nullable ScannerCommandCallback cb) -> void + +Simulates a press on a hardware-trigger, firing the beam that will read barcodes. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +:::{method} releaseScanTrigger(@Nullable ScannerCommandCallback cb) -> void + +Ends the effect of `pressScanTrigger()`. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +### The `WithIlluminationSupport` interface + +:::{method} Scanner.getIlluminationSupport() -> WithIlluminationSupport +::: + +Extra interface implemented by scanners that support illumination. + +It exposes the following methods: + +:::{method} enableIllumination(@Nullable ScannerCommandCallback cb) -> void + +If the device used has a way to illuminate the target, enable it. Idempotent. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +:::{method} disableIllumination(@Nullable ScannerCommandCallback cb) -> void + +Opposite of `enableIllumination()`. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +:::{method} toggleIllumination(@Nullable ScannerCommandCallback cb) -> void + +See `enableIllumination()` and `disableIllumination()`. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +::: + +:::{method} isIlluminationOn() -> boolean + +True if the illumination method is active. + +:returns: True if the illumination method is active. +::: + +### The `WithLedSupport` interface + +:::{method} Scanner.getLedSupport() -> WithLedSupport +::: + +Extra interface implemented by scanners that support LED customization. + +It exposes the following methods: + +:::{method} ledColorOn(ScannerLedColor color, @Nullable ScannerCommandCallback cb) -> void + +Turns a LED color on. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +:param ScannerLedColor color: The color to turn on. +::: + +:::{method} ledColorOff(ScannerLedColor color, @Nullable ScannerCommandCallback cb) -> void + +Turns a LED color off. + +:param ScannerCommandCallback cb: The callback to call with the result of the operation. May be null +or absent. +:param ScannerLedColor color: The color to turn off. +::: + +### The `WithInventorySupport` interface + +:::{method} Scanner.getInventorySupport() -> WithInventorySupport +::: + +Extra interface implemented by scanners that offer inventory information. + +It exposes the following methods: + +:::{method} getStatus(String key) -> String + +Get an inventory/status value. For example battery serial number, device MAC, etc. Keys are usually +constants exported by drivers. Data returned may come from a local cache. + +:param String key: The requested inventory key. +:returns: The corresponding value, or null if the key is not supported by this scanner. +::: + +:::{method} getStatus(String key, boolean allowCache) -> String +:no-index: + +Get an inventory/status value. For example battery serial number, device MAC, etc. Keys are usually +constants exported by drivers. + +:param String key: The requested inventory key. +:param boolean allowCache: If false the driver is not allowed to use a cache and MUST fetch fresh +data from the device. +:returns: The corresponding value, or null if the key is not supported by this scanner. +::: + +:::{method} getStatus() -> Map +:no-index: + +:returns: all inventory/status data known by the scanner. May be empty but not null. +::: + +The following inventory keys are expected. It is up to each `Scanner` implementation to interpret +them: + +:::{cpp:var} String SCANNER_STATUS_SCANNER_SN = "SCANNER_STATUS_SCANNER_SN" +::: +:::{cpp:var} String SCANNER_STATUS_SCANNER_MODEL = "SCANNER_STATUS_SCANNER_MODEL" +::: +:::{cpp:var} String SCANNER_STATUS_BATTERY_SN = "SCANNER_STATUS_BATTERY_SN" +::: +:::{cpp:var} String SCANNER_STATUS_BATTERY_MODEL = "SCANNER_STATUS_BATTERY_MODEL" +::: +:::{cpp:var} String SCANNER_STATUS_BATTERY_WEAR = "SCANNER_STATUS_BATTERY_WEAR" +::: +:::{cpp:var} String SCANNER_STATUS_BATTERY_CHARGE = "SCANNER_STATUS_BATTERY_CHARGE" +::: +:::{cpp:var} String SCANNER_STATUS_FIRMWARE = "SCANNER_STATUS_FIRMWARE" +::: +:::{cpp:var} String SCANNER_STATUS_BT_MAC = "SCANNER_STATUS_BT_MAC" +::: \ No newline at end of file diff --git a/docs/api/scanner_activity.md b/docs/api/scanner_activity.md new file mode 100644 index 00000000..c9e134ae --- /dev/null +++ b/docs/api/scanner_activity.md @@ -0,0 +1,167 @@ +# The Scanner activity + +The simplest way to start using **enioka Scan** in an activity is simply to inherit from the +`ScannerCompatActivity` class. This class implements the +[`ScannerClient` interface](scanner_service.md#the-scannerclient-interface) and handles the scanner +service bindings and life cycle. It also exposes a default UI to print scan results, status messages +and some buttons to manually control the scanner device from the app. + +Most of its methods are protected and can be overridden if needed. Mainly, methods from the +[`ScannerClient` interface](scanner_service.md#the-scannerclient-interface) should be replaced with +your own to adapt the way scan results are handled by your application. + +By default, the activity will use the +[default search options](scanner_service.md#the-scannersearchoptions-class) to initiate scanner +search. There are two ways to change these options: override the +[`getServiceInitExtras()` method](#scannercompatactivity-methods), and passing the search options as +extras to the intent starting the activity. In case both are used, the method is applied first, then +intent extras. + +This page will describe attributes and methods specific to this activity, disregarding overrides. +For most users, it is recommended to only change the value of attributes or interface methods, as +overriding class methods may cause unexpected side-effects. For example, using a custom layout only +requires changing the corresponding layout ID attributes. + +## `ScannerCompatActivity` attributes + +:::{cpp:var} boolean laserModeOnly = false + +If true, the activity will not switch to using the camera, even if no other scanners are available. +::: + +:::{cpp:var} boolean enableScan = true + +If false, the activity will behave like a standard `AppCompatActivity`. +::: + +:::{cpp:var} boolean goToCamera = false + +If true, the activity will directly switch to using the camera. This boolean is automatically set +to true if the scanner service finds no scanner after the first search. +::: + +:::{cpp:var} boolean useBluetooth = true + +If true, the activity will check that the application has bluetooth permissions before starting the +service. **This does not affect scanner search options**. +::: + +:::{cpp:var} int layoutIdLaser = R.layout.activity_main + +The layout used by the activity when using regular scanner devices. May be replaced with your own. +::: + +:::{cpp:var} int layoutIdCamera = R.layout.activity_main_alt + +The layout used by the activity when using the camera as a scanner. May be replaced with your own. +::: + +:::{cpp:var} int cameraViewId = R.id.camera_scan_view + +The ID of the [`CameraBarcodeScanView`](camera.md#the-camerabarcodescanview-class) inside the +`layoutIdCamera` layout. +::: + +:::{cpp:var} int cameraToggleId = R.id.scanner_bt_camera + +The ID of the optional ImageButton on which to press to manually switch to camera mode. +::: + +:::{cpp:var} int flashlightViewId = R.id.scanner_flashlight + +The ID of the optional ImageButton on which to press to toggle the flashlight/illumination. +::: + +:::{cpp:var} int scannerModeToggleViewId = R.id.scanner_switch_zxing + +The ID of the optional ImageButton on which to press to toggle the zxing/zbar camera scan library. +::: + +:::{cpp:var} int scannerModeTogglePauseId = R.id.scanner_switch_pause + +The ID of the optional toggle button on which to press to pause/unpause the scanner. +::: + +:::{cpp:var} int keyboardOpenViewId = R.id.scanner_bt_keyboard + +The ID of the optional toggle button on which to display the manual input fragment. +::: + +:::{cpp:var} ManualInputFragment manualInputFragment; + +An optional fragment allowing to input a value with the soft keyboard (for cases when scanners do +not work). +::: + +:::{cpp:var} List autocompletionItems = new ArrayList<>() + +Auto completion items for manual input (with manualInputFragment). +::: + +:::{cpp:var} int threshold = 5 + +How many characters should be entered before auto-completion starts. +::: + +:::{cpp:var} ScannerServiceApi scannerService; + +The instance of the bound scanner service, can be used to access service methods but should not be +replaced. + +Initialized by the `onStart()` and `onResume()` methods. +::: + +:::{cpp:var} CameraBarcodeScanViewScanner cameraScanner; + +The instance of the camera scanner, can be used to access camera methods but should not be replaced. +`CameraBarcodeScanViewScanner` is a simple provider-less implementation of the +[`Scanner` interface](scanner.md#the-scanner-interface). + +Initialized by the [`initCamera()`](#scannercompatactivity-methods) method. +::: + +## `ScannerCompatActivity` methods + +:::{method} getServiceInitExtras() -> Bundle + +:returns: The [search options](scanner_service.md#the-scannersearchoptions-class) that will be used + when starting the service. These options may be overriden by intent extras when launching the + activity. +::: + +:::{method} setAutocompletion(List autocompletion, int threshold) -> void + +**Inserts** the given autocompletion strings into the `autocompletionItems` and updates the +autocompletion threshold. + +:param List autocompletion: The autocompletion items to add. +:param int threshold: The new threshold. +::: + +:::{method} setAutocompletionItems(List items, int threshold) -> void + +**Replaces** `autocompletionItems` with the given list and updates the autocompletion threshold. + +:param List autocompletion: The autocompletion items to use. +:param int threshold: The new threshold. +::: + +:::{method} initCamera() -> void + +Switches the activity to camera mode. After this method is called, `goToCamera` is set to true. +::: + +:::{method} anyScannerSupportsIllumination() -> boolean + +Checks whether any available scanner supports Illumination. +::: + +:::{method} anyScannerHasIlluminationOn() -> boolean + +Checks whether any available scanner has Illumination toggled on. +::: + +:::{seealso} + +* The [`ScannerClient` interface](scanner_service.md#the-scannerclient-interface) documentation. +::: \ No newline at end of file diff --git a/docs/api/scanner_callbacks.md b/docs/api/scanner_callbacks.md new file mode 100644 index 00000000..3947ac41 --- /dev/null +++ b/docs/api/scanner_callbacks.md @@ -0,0 +1,210 @@ +# API callbacks + +Most **enioka Scan** actions use callbacks to communicate their result to your code. In the case of +scanner-related callbacks, proxy classes exist to wrap callback implementations and ensure their +methods are called on the UI thread. + +This page regroups all callback interfaces from **enioka Scan**. + +## The `ScannerCommandCallback` interface and `ScannerCommandCallbackProxy` class + +Callback used to inform of the result of scanner commands, usually optional. + +:::{method} onSuccess() -> void + +Called when the command was completed, and if it received a positive answer (when applicable). +::: + +:::{method} onFailure() -> void + +Called when the command failed, either through or SDK error or negative answer (when applicable). +::: + +:::{method} onTimeout() -> void + +Called when the command's expected answer did not arrive in time (usually only with BT scanners). +::: + +## The `ScannerConnectionHandler` interface and `ScannerConnectionHandlerProxy` class + +Callback used during the scanner search to retrieve created +[`Scanner` instances](scanner.md#the-scanner-interface). + +:::{method} scannerConnectionProgress(String providerKey, String scannerKey, String message) -> void + +A SDK-specific localized message signaling a change during the search for a scanner. + +:param String providerKey: The identifier of the provider/SDK +:param String scannerKey: A unique identifier for the scanner being connected +:param String message: An SDK-specific message (localized) +::: + +:::{method} scannerCreated(String providerKey, String scannerKey, Scanner s) -> void + +Called when a scanner was found and created. Depending on +[`ScannerSearchOptions.returnOnlyFirst`](scanner_service.md#the-scannersearchoptions-class) may be +called multiple times. + +:param String providerKey: The identifier of the provider/SDK +:param String scannerKey: A unique identifier for the scanner being connected +:param Scanner s: The scanner instance +::: + +:::{method} noScannerAvailable() -> void + +Called when there is no scanner available on the device. `endOfScannerSearch()` is always called +after this. +::: + +:::{method} endOfScannerSearch() -> void + +Called when the search for scanners in the different providers is over. +::: + +## The `ScannerDataCallback` interface and `ScannerDataCallbackProxy` class + +Callback used to handle the data read by the scanner. + +:::{method} onData(Scanner s, List data) -> void +:no-index: + +Called whenever data is read by the scanner. + +:param Scanner s: The scanner from which data was read +:param List data: The data read by the scanner +::: + +## The `ScannerInitCallback` interface and `ScannerInitCallbackProxy` class + +Callback handling scanner init events. + +:::{method} onConnectionSuccessful(Scanner s) -> void + +Called whenever a scanner successfully connected. + +:param Scanner s: The connected scanner. +::: + +:::{method} onConnectionFailure(Scanner s) -> void + +Called whenever a scanner could not connect. + +:param Scanner s: The disconnected scanner. +::: + +## The `ScannerStatusCallback` interface and `ScannerStatusCallbackProxy` class + +:::{cpp:enum} Status + +This enum represents the scanner's lifecycle, as well as the current status of the scanner, scanner +provider and scanner service's search. Some elements may not be used depending on which scanner SDK +is used. + +:WAITING: The scanner provider is waiting for a connection. +:CONNECTING: The scanner is in the process of connecting. +:RECONNECTING: The scanner disconnected but is trying to reconnect. +:CONNECTED: The scanner has finished connecting. +:INITIALIZING: The scanner is in the process of initializing. +:INITIALIZED: The scanner has finished initializing. +:READY: The scanner is ready to scan and waiting to be used. +:SCANNING: The scanner is in the process of scanning. +:PAUSED: The scanner is connected, initialized and enabled but not ready to scan. +:DISABLED: The scanner is connected and initialized but has been disabled and cannot be used. +:FAILURE: The scanner is no longer available after a critical error occurred, usually during + connection or initialization. +:DISCONNECTED: The scanner disconnected and can no longer be used. +:UNKNOWN: The scanner is in an unknown status. +:SERVICE_PROVIDER_SEARCH_OVER: ScannerService's provider search is over. +:SERVICE_SDK_SEARCH_OVER: ScannerService's SDK search is over. +:SERVICE_SDK_SEARCH_NOCOMPATIBLE: ScannerService's SDK search found no scanner. + +::: + +:::{method} onStatusChanged(Scanner scanner, Status newStatus) -> void + +Called whenever the scanner has changed status. +:param Scanner scanner: The updated scanner. May be null if the scanner has not yet been created. +:param Status newStatus: The new scanner status. +::: + +## The `ProviderDiscoveredCallback` interface and `ProviderDiscoveredCallbackProxy` class + +Callback used during provider discovery (before scanner search). + +:::{method} onDiscoveryDone() -> void + +Called once all providers are discovered. +::: + +## The `ScannerProvider.ProviderCallback` interface + +Callback used by a scanner provider to inform the search process whether it is available and +able to connect to a scanner. + +:::{method} onScannerCreated(String providerKey, String scannerKey, Scanner s) -> void + +Called when the provider has finished creating a scanner. + +:param String providerKey: The identifier of the provider/SDK +:param String scannerKey: A unique identifier for the scanner being connected +:param Scanner s: The scanner instance +::: + +:::{method} connectionProgress(String providerKey, String scannerKey, String message) -> void + +Send a localized status message to the end user. + +:param String providerKey: The identifier of the provider/SDK +:param String scannerKey: A unique identifier for the scanner being connected +:param String message: The message (localized) +::: + +:::{method} onProviderUnavailable(String providerKey) -> void + +Called when the provider has determined it cannot run (is not available) on this device and should +not be revived. + +:param String providerKey: The identifier of the provider/SDK +::: + +:::{method} onAllScannersCreated(String providerKey) -> void + +Called if the provider can run, and all scanners have already been created. + +:param String providerKey: The identifier of the provider/SDK +::: + +:::{method} isAlreadyConnected(BluetoothDevice device) -> void + +Called if a bluetooth device was already taken by a previous provider. + +:param BluetoothDevice device: The busy bluetooth device +::: + +## The `BtSppScannerProvider.ManagementCallback` interface + +Callback used by bluetooth scanner providers to inform the master bluetooth provider that it cannot +manage the device it was tested for. + +:::{method} canManage(Scanner libraryScanner) -> void + +Called if the provider can manage the device. + +:param Scanner libraryScanner: The corresponding +[`Scanner` instance](scanner.md#the-scanner-interface). +::: + +:::{method} cannotManage() -> void + +Called if the provider can not manage the device. +::: + +## The `CameraBarcodeScanView.ResultHandler` interface + +Callback used by the camera reader when a barcode is read from the camera feed. + +:::{method} handleScanResult(String result, BarcodeType type) -> void + +:param String result: The barcode data +:param BarcodeType type: The barcode symbology +::: \ No newline at end of file diff --git a/docs/api/scanner_provider.md b/docs/api/scanner_provider.md new file mode 100644 index 00000000..be91807f --- /dev/null +++ b/docs/api/scanner_provider.md @@ -0,0 +1,127 @@ +# The ScannerProvider API + +The Scanner Provider is the entrypoint of an SDK. Its purpose is to check if the SDK is able to +interact with a device, and handle the [`Scanner`](scanner.md) creation. + +In most cases, users will not need to interact with a provider directly, but adding compatibility to +more devices will require adding a new provider. + +This page regroups all provider-related interfaces and helper classes. + +## The `ScannerProvider` interface + +The core methods of a provider common between, all types of devices. + +:::{method} getScanner(Context ctx, ProviderCallback cb, ScannerSearchOptions options) -> void + +This method will make the provider check whether it is compatible with any scanning device while +matching requirements imposed by the search options. If it is able to connect to a device, the +provider will use the callback to notify its availability and return a +[`Scanner` instance](scanner.md#the-scanner-interface). + +:param Context ctx: The application context. +:param ProviderCallback cb: The `ProviderCallback` used to notify the Scanner Service whether this + SDK is available (as in, is able to connect to any scanning device), and the current status of + [`Scanner` instance](scanner.md#the-scanner-interface) creation. +:param ScannerSearchOptions options: The search options the provider may use to further refine its + compatibility checks. +::: + +:::{method} getKey() -> String + +:returns: The unique key which identifies this provider. +::: + +:::{seealso} + +Check the documentation of the +[`ProviderCallback` interface](scanner_callbacks.md#the-scannerproviderprovidercallback-interface) +for details on how the provider will communicate its status with the Scanner Service. +::: + +## The `IntentScannerProvider` abstract class + +In order to facilitate discovery for integrated devices, the `IntentScannerProvider` abstract +class provides an implementation of the `ScannerProvider.getScanner()` to check various Android +mechanisms and figure out if the current device is compatible with the SDK. + +A provider for an integrated scanner should extend this class and override the following methods: + +:::{method} createNewScanner(Context ctx, ScannerSearchOptions options) -> Scanner + +This method is called by `ScannerProvider.getScanner()` to create the +[`Scanner` instance](scanner.md#the-scanner-interface). + +:param Context ctx: The application context. +:param ScannerSearchOptions options: The search options the provider used to confirm compatibility. +:returns: The [`Scanner`](scanner.md) instance of the implementation used by this SDK. +::: + +:::{method} configureProvider() -> void + +This method is called by `ScannerProvider.getScanner()` to set what should be tested to know if the +device is compatible with an SDK. There are four different mechanisms that can be tested, if +multiple of them are set, ***all*** must be valid for the SDK to be considered compatible. +::: + +The following variables may be set in `configureProvider()` to influence the compatibility check: + +:::{cpp:var} String intentToTest = null + +If set, the provider will be compatible only if the given Android Intent has a matching listener +on the device. +::: + +:::{cpp:var} List specificDevices = new ArrayList<>(0) + +If set, the provider will be compatible only if the value of `android.os.Build.MODEL` exactly +matches one of the values contained in the list. +::: + +:::{cpp:var} String appPackageToTest = null + +If set, the provider will be compatible only if the given application package name is found and +enabled on the device. +::: + +:::{cpp:var} String serviceToTest = null + +If set, the provider will be compatible only if the given service package name can be started by the +library. +::: + +## The case of Bluetooth, and the `BtSppScannerProvider` interface + +Because bluetooth devices require extra steps to detect, the provider discovery for such devices is +a bit different. A single `SerialBtScannerProvider` class implements the `ScannerProvider` +interface to handle system-level bluetooth configuration, and the actual providers for SDKs +targetting bluetooth devices need to implement the `BtSppScannerProvider` interface. + +A bluetooth provider must implement the following methods: + +:::{method} getKey() -> String +:no-index: + +:returns: The unique key which identifies this provider. +::: + +:::{method} canManageDevice(BluetoothScanner device, ManagementCallback callback) -> void + +Tests whether a scanner is compatible with the provider. Must complete in under 50ms. + +:param BluetoothScanner device: The "raw" bluetooth device for which to test SDK compatibility +:param ManagementCallback callback: The callback used to notify whether the compatibility check + was successful or not. +::: + +:::{method} getInputHandler() -> ScannerDataParser + +:returns: The parser which should be used to parse bluetooth messages from compatible devices. +::: + +:::{seealso} + +Check the documentation of the +[`ManagementCallback` interace](scanner_callbacks.md#the-btsppscannerprovidermanagementcallback-interface) +for details on how the bluetooth provider will communicate its status with the Scanner Service. +::: \ No newline at end of file diff --git a/docs/api/scanner_service.md b/docs/api/scanner_service.md new file mode 100644 index 00000000..68f9a4f2 --- /dev/null +++ b/docs/api/scanner_service.md @@ -0,0 +1,295 @@ +# The ScannerService API + +The `ScannerService` is responsible for handling the search of an available scanner device and SDK +for the end user. Once any is found, it can be made available to the user in the form of a +[`Scanner`](scanner) instance. + +This page regroups all service-related interfaces and helper classes. + +## The `ScannerServiceApi` interface + +The `ScannerServiceApi` interface exposes methods that give the user control on the scanner search +and on the handling of found scanners. + +:::{method} registerClient(ScannerClient client) -> void + +Registers a [`ScannerClient`](#the-scannerclient-interface) implementation to the service. The +service will call this client's callbacks whenever applicable. + +:param ScannerClient client: The client to register. +::: + +:::{method} unregisterClient(ScannerClient client) -> void + +Unregisters a [`ScannerClient`](#the-scannerclient-interface) implementation from the service. The +service will no no longer call this client's callbacks. + +:param ScannerClient client: The client to unregister. +::: + +:::{method} restartScannerDiscovery() -> void + +Disconnects all currently-connected [scanners](scanner.md#the-scanner-interface) then starts the +initialization process all over again. +::: + +:::{method} restartProviderDiscovery() -> void + +Clears the cache of discovered [scanner providers](scanner_provider.md#the-scannerprovider-api) and +re-starts their discovery. This is a costly operation. +::: + +:::{method} getAvailableProviders() -> List + +:returns: The list of keys of currently-available scanner providers, useful to tune scanner search +options at runtime. +::: + +:::{method} getConnectedScanners() -> List + +:returns: The list of keys of currently-connected scanners, allowing their direct manipulation +through the [`Scanner` API](scanner.md#the-scanner-interface). +::: + +:::{method} updateScannerSearchOptions(ScannerSearchOptions newOptions) -> void + +Updates the service's scanner search options. + +:param ScannerSearchOptions newOptions: The new options to use during future searches. +::: + +:::{method} pause() -> void +:no-index: + +Calls the [`Scanner.pause()`](scanner.md#the-scanner-interface) method for all connected scanners. +::: + +:::{method} resume() -> void +:no-index: + +Calls the [`Scanner.resume()`](scanner.md#the-scanner-interface) method for all connected scanners. +::: + +:::{method} disconnect() -> void +:no-index: + +Calls the [`Scanner.disconnect()`](scanner.md#the-scanner-interface) method for all connected +scanners. + +Should scanners be needed again, `restartScannerDiscovery()` will need to be +called first. +::: + +## The `ScannerClient` interface + +The Scanner Service, like any service, works as a standalone unit in the background. While it can +be manipulated through the `ScannerServiceApi` interface, it needs to register a series of callbacks +to send information to your application. These callbacks are grouped in the `ScannerClient` +interface, which should be implemented by the part of your code responsible for handling the service +lifecycle and barcode reads. + +:::{seealso} +This interface extends `ScannerStatusCallback` and needs to implement its method, see more in the +[callbacks documentation](scanner_callbacks.md#the-scannerstatuscallback-interface-and-scannerstatuscallbackproxy-class). +::: + +:::{method} onScannerInitEnded(int count) -> void + +This callback is used once the initialization of scanners is over. It will be called +retro-actively on new clients if at least one scanner initialization happened. + +:param int count: The amount of initialized scanners. +::: + +:::{method} onProviderDiscoveryEnded() -> void + +This callback is used once the discovery of scanner providers is over and scanners are ready +to be used. It will be called retro-actively on new clients if at least one provider discovery +happened. +::: + +:::{method} onData(List data) -> void + +This callback is used whenever a connected scanner successfully reads data. + +:param List data: The barcodes read by the scanner. +::: + +## The `ScannerSearchOptions` class + +The `ScannerSearchOptions` provides a clear list of options used to refine the scanner search. Each +attribute from `ScannerSearchOptions` corresponds to an intent extra used by `ScannerServiceApi`. + +:::{method} defaultOptions() -> ScannerSearchOptions + +:returns: an instance of `ScannerSearchOptions` using the default values described below. Equivalent + to a default constructor call. +::: + +:::{cpp:var} boolean startSearchOnServiceBind = true + +If true, the service will start searching for scanners immediately upon binding. + +If false, the service will only discover available providers without requesting scanners +immediately. + +Corresponds to the `ScannerServiceApi.EXTRA_START_SEARCH_ON_SERVICE_BIND` extra. +::: + +:::{cpp:var} boolean waitDisconnected = true + +If a scanner is known but not currently available, wait for it. If false, consider the scanner +unavailable immediately. + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_WAIT_DISCONNECTED_BOOLEAN` extra. +::: + +:::{cpp:var} boolean returnOnlyFirst = true + +If true, will only connect to the first scanner available (or reporting it may become available if +`waitDisconnected` is true). + +If false, all available scanners will be used. + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_RETURN_ONLY_FIRST_BOOLEAN` extra. +::: + +:::{cpp:var} boolean useBlueTooth = true + +If true, bluetooth devices will be searched for compatible scanners. + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_ALLOW_BT_BOOLEAN` extra. +::: + +:::{cpp:var} boolean allowIntentDevices = false + +If true, providers using another application for controlling scanners will be allowed. +This is important as most of the time, it is impossible to say if these apps are actually installed, +and therefore it is impossible to detect the presence of an actual scanner or not. + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_ALLOW_INTENT_BOOLEAN` extra. + +::::{seealso} + +Check [`IntentScannerProvider`](scanner_provider.md#the-intentscannerprovider-abstract-class) for +more details on how this type of device is checked for compatibility. +:::: + +::: + +:::{cpp:var} boolean allowLaterConnections = true + +If true, some providers may retrieve scanners after initial search is over. + +Mostly used to let bluetooth providers keep listening for incoming devices after search. + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_KEEP_SEARCHING_BOOLEAN` extra. +::: + +:::{cpp:var} boolean allowInitialSearch = true + +If true, some providers may retrieve scanners while the initial search is taking place. + +Mostly used to let bluetooth providers contact known devices during search. + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_ALLOW_INITIAL_SEARCH_BOOLEAN` extra. +::: + +:::{cpp:var} boolean allowPairingFlow = false + +If true, the providers which needs a pairing done by their own SDK (for example, BLE on-the-fly +pairing) will be allowed to do so. + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_ALLOW_PAIRING_FLOW_BOOLEAN` extra. +::: + +:::{cpp:var} Set allowedProviderKeys = null + +Restricts the search to this list of providers. Ignored if null or empty. + +Mainly used to whitelist known expected devices, or to refine the scanner search using the results +of [`ScannerServiceApi.getAvailableProviders()`](#the-scannerserviceapi-interface). + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_ALLOWED_PROVIDERS_STRING_ARRAY` extra. +::: + +:::{cpp:var} Set excludedProviderKeys = null + +The providers inside this list will never be used. Ignored if null or empty. + +Mainly used to blacklist known unwanted devices, or to refine the scanner search using the results +of [`ScannerServiceApi.getAvailableProviders()`](#the-scannerserviceapi-interface). + +Corresponds to the `ScannerServiceApi.EXTRA_SEARCH_EXCLUDED_PROVIDERS_STRING_ARRAY` extra. +::: + +:::{cpp:var} Set symbologySelection = null + +An array of [`BarcodeType`](others.md#the-barcodetype-enum) to activate. Ignored if null or empty. + +Corresponds to the `ScannerServiceApi.EXTRA_SYMBOLOGY_SELECTION` extra. +::: + +It also exposes methods to simplify the process of converting options into Intent extras, and vice +versa. + +:::{method} fromIntentExtras(Intent intent) -> ScannerSearchOptions + +Sets the option's attributes based on a given intent's extras. If an extra is missing, the current +value of the attribute is preserved. + +:param Intent intent: The Intent to read. +:returns: this +::: + +:::{method} toIntentExtras(Intent intent) -> void + +Updates an intent's extras based on the current option's attributes. + +:param Intent intent: The Intent to update. +::: + + +## The `ScannerServiceBinderHelper` class + +To make the process of binding your application to the Scanner Service easier, the +`ScannerServiceBinderHelper` class exposes a series of methods you can use. + +:::{method} defaultServiceConfiguration() -> Bundle + +Static method. + +:returns: The default service search options used everywhere else in the library. A new bundle is +returned on each call. +::: + +:::{method} bind(Application a, Bundle extra) -> ScannerServiceBinderHelper + +Static method. Binds to the Scanner Service using the `defaultServiceConfiguration()` extras. + +:param Application a: The application to which the service will be bound. +:param Bundle extra: Intent extras passed to the service on bind. See + [`ScannerSearchOptions.toIntentExtras()`](#the-scannersearchoptions-class). +:returns: The `ScannerServiceBinderHelper` instance that bound to the service. +::: + +:::{method} bind(Application a) -> ScannerServiceBinderHelper +:no-index: + +Static method. Binds to the Scanner Service using the `defaultServiceConfiguration()` extras. + +:param Application a: The application to which the service will be bound. +:returns: The `ScannerServiceBinderHelper` instance that bound to the service. +::: + +:::{method} getScannerService() -> ScannerServiceApi + +:returns: The `ScannerServiceApi` instance bound to this `ScannerServiceBinderHelper` instance. +:throws IllegalStateException: If no service is bound when called. +::: + +:::{method} disconnect() -> void +:no-index: + +Disconnects the currently-connected service from this `ScannerServiceBinderHelper` instance. +::: \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 4e9d86cd..ca69c73c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,15 +1,26 @@ # coding: utf-8 source_suffix = { - '.rst': 'restructuredtext', - '.txt': 'markdown', '.md': 'markdown', } source_encoding = 'utf-8' master_doc = 'index' -extensions = ['myst_parser'] -myst_enable_extensions = ['colon_fence', 'strikethrough'] +extensions = [ + 'myst_parser', # Markdown support +] + +myst_enable_extensions = [ + 'colon_fence', # Alternative directive call using :::{directive} ::: + 'strikethrough', # Text strikethrough using ~~text~~ + 'fieldlist', # Better handling of manyfields blocks, useful for API documentation +] + +myst_heading_anchors = 4 + +suppress_warnings = [ + 'myst.strikethrough', # We only generate HTML docs, ignore non-html warnings. +] project = 'enioka Scan' copyright = '2017-2023, enioka Haute Couture' diff --git a/docs/dependencies.md b/docs/dependencies.md new file mode 100644 index 00000000..4efbc6de --- /dev/null +++ b/docs/dependencies.md @@ -0,0 +1,129 @@ +# Dependencies and compatible devices + +**enioka Scan** is split over multiple AAR dependencies, all available through the Maven Central +repository, allowing you to only include the compatibility layers you need. + +The compatibility matrix at the end of this page explains more clearly what devices are supported by +each scanner provider, and how each device is detected by the library. + +While most providers are standalone and include their own open-source SDK, some providers still +require proprietary code in order to work with their device. Depending on devices, this code can +take the form of a JAR or AAR file made available to you by the vendor, or a companion app, service +or SDK installed separately on the device. In the case of a library file, you will need to +manually add the archive to your application's classpath. If a required SDK is missing, the +provider will simply not show up as compatible during use. + +## enioka Scan artefacts + +Each artefact can be imported to your project as `com.enioka.scanner:::aar`. + +### `scanner` + +Core components of the library, **required** for it to work. It includes the scanner APIs, default +scanning activity and service, and camera-as-a-scanner functionality. + +It also includes the following scanner providers: + +- `AthesiE5LProvider`: Support for the Athesi E5L integrated scanner. +- `AthesiHHTProvider`: Support for the Athesi SPA43 integrated scanner. +- `BluebirdProvider`: Support for Bluebird integrated scanners. +- `BT_GeneralScanProvider`: Support for GeneralScan bluetooth scanners. +- `BT_HoneywellOssSppProvider`: Support for Honeywell bluetooth scanners. +- ~~`BT_PostechProvider`~~: Support for Postech ring scanners. This provider could not be properly + tested and as such is disabled in code. To re-enable it, you will need to fork the project and + un-comment the corresponding lines in the `AndroidManifest.xml`. +- `BT_ZebraOssSPPProvider`: Support for Zebra scanners using the SSI protocol, working over + Bluetooth Classic +- `BT_ZebraOssATTProvider`: Support for Zebra scanners using the SSI protocol, working over + Bluetooth Low Energy. +- `HoneywellOssIntegratedProvider`: Support for Honeywell integrated scanners. +- `ProgloveProvider`: Support for Proglove scanners. + +:::{note} + +These providers will eventually be split into their own separate artefacts in order to +reduce the weight of the core dependency and let users pick only the providers they need. +::: + +### `provider-cs-honeywell` + +Includes the `HONEYWELL_AIDC` provider, adding support for Honeywell AIDC (Intermec) integrated +devices. + +This provider requires the "Android data collection" (AIDC), or Intermec, SDK, which needs to be +installed either in your application's classpath or device to resolve `com.honeywell.aidc.` +packages. + +This provider also requires the `com.honeywell.decode.DecodeService` Android service to be installed +and enabled on your device. + +### `provider-cs-koamtac` + +Includes the `Koamtac` provider, adding support for Koamtac KDC-family bluetooth +scanners. + +This provider requires an external SDK provided by Koamtac, which needs to be included in your +application's classpath to resolve `koamtac.kdc.sdk.*` packages. + +### `provider-cs-m3` + +Includes the `M3RingScannerProvider` provider, adding support for M3 ring devices. + +This provider requires an external SDK, provided by M3Mobile, which needs to be included in your +application's classpath to resolve `com.m3.ringscannersdk.*` packages. + +This provider also requires the `Ring Scanner` companion app, which should be installed and enabled +on your device to resolve `com.m3.ringscanner.*` packages. + +### `provider-cs-zebra` + +Includes the following providers: + +- `BtZebraProvider`: Support for most Zebra bluetooth devices. + + This provider requires an external SDK, provided by Zebra, which needs to be included in your + application's classpath to resolve `com.zebra.scannercontrol.*` packages. + +- `Zebra EMDK`: Support for Zebra EMDK integrated devices. + + This provider requires an external SDK, which should be installed on your device by default, to + resolve `com.symbol.emdk.*` packages. + +### `provider-cs-zebra-dw` + +Includes the `ZebraDwProvider` provider, adding support for most integrated devices supporting +the Zebra Datawedge service. + +This provider requires the Datawedge service, in the form of the `com.symbol.datawedge` application +package, to be installed and enabled on your device. + +## Compatibility matrix + +This table aggregates all currently-available scanner providers and lists which devices they +support, their external requirements if applicable, and how each provider detects compatible +devices. + +:::{table} +:widths: auto +:align: center + +| Artefact | Provider name | Supported devices | Tested devices | Device type | External requirements | Device compatible if | +|-------------------------|----------------------------------|-----------------------------------------------------|------------------------------------|---------------|----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| `scanner` | `CAMERA_SCANNER` (no provider) | Any device with a camera | Smartphones, integrated devices... | Camera | | Device has a camera | +| `scanner` | `AthesiE5LProvider` | Athesi E5L | Athesi E5L | Integrated | | Device name is strictly `RD50TE` | +| `scanner` | `AthesiHHTProvider` | Athesi SPA43 | Athesi SPA43 | Integrated | | Device name is strictly `SPA43LTE` | +| `scanner` | `BluebirdProvider` | Bluebird integrated scanners | Bluebird EF500 | Integrated | Bluebird service (should be preinstalled on device) | Intent `kr.co.bluebird.android.bbapi.action.BARCODE_OPEN` has a listener | +| `scanner` | `BT_GeneralScanProvider` | GeneralScan bluetooth ring scanners | GeneralScan GS R5000BT | BT Classic | | Bluetooth device responds to "Get device ID" command | +| `scanner` | `BT_HoneywellOssSppProvider` | Honeywell bluetooth scanners | Honeywell Voyager 1602g | BT Classic | | Bluetooth device responds to "Get firmware" command | +| `scanner` | `BT_ZebraOssSPPProvider` | Zebra bluetooth scanners using the SSI protocol | Zebra RS5100 | BT Classic | | Bluetooth device is not BLE and responds to "CAPABILITIES_REQUEST" command | +| `scanner` | `BT_ZebraOssATTProvider` | Zebra bluetooth scanners using the SSI protocol | Zebra RS5100 | BT Low Energy | | Bluetooth device is BLE and responds to "CAPABILITIES_REQUEST" command | +| `scanner` | `HoneywellOssIntegratedProvider` | Honeywell integrated scanners | Honeywell EDA52 | Integrated | Honeywell service (should be preinstalled on device) | Intent `com.honeywell.decode.DecodeService` has a listener | +| `scanner` | `ProgloveProvider` | Proglove devices interfacing with their application | Proglove Glove Mark II | BT Low Energy | Proglove application | Application package `de.proglove.connect` exists | +| `provider-cs-honeywell` | `HONEYWELL_AIDC` | Honeywell AIDC / Intermec integrated devices | Honeywell EDA50 | Integrated | AIDC SDK, Honeywell service (should be preinstalled on device) | Intent `com.honeywell.decode.DecodeService` has a listener and AIDC SDK exists | +| `provider-cs-koamtac` | `Koamtac` | Koamtac bluetooth KDC devices | Koamtac KDC180 | BT Low Energy | Koamtac SDK | Class `koamtac.kdc.sdk.KDCReader` exists and scanner device is found | +| `provider-cs-m3` | `M3RingScannerProvider` | M3Mobile bluetooth ring scanners | M3 Ring Scanner | BT Classic | Ring Scanner SDK, Ring Scanner application | Class `com.m3.ringscannersdk.RingScannerService` exists, application package `com.m3.ringscanner` exists, scanner device is found | +| `provider-cs-zebra` | `BtZebraProvider` | Zebra bluetooth scanners | Zebra RS6000, RS5100 | BT Classic | Zebra SDK | Class `com.zebra.scannercontrol.SDKHandler` exists and scanner device is found | +| `provider-cs-zebra` | `Zebra EMDK` | Zebra EMDK integrated scanners | Zebra TC25 | Integrated | Zebra EMDK SDK (should be preinstalled on device) | Class `com.symbol.emdk.EMDKManager` exists | +| `provider-cs-zebra-dw` | `ZebraDwProvider` | Any device compatible with Zebra Datawedge | Zebra TC25, Zebra TC27 | Integrated | Zebra Datawedge service (should be preinstalled on device) | Application package `com.symbol.datawedge` exists | + +::: \ No newline at end of file diff --git a/docs/develop.md b/docs/develop.md new file mode 100644 index 00000000..071428bf --- /dev/null +++ b/docs/develop.md @@ -0,0 +1,90 @@ +# Development + +This page will summarize how to modify **enioka Scan**, whether it is to contribute to the project +or for your own needs. + +## Developing for Android + +In order to start developing and testing the library: +- Have [Android Studio](https://developer.android.com/studio) installed and open the project with it +- Connect a compatible android device via USB, it should then show up in Android Studio's device + manager +- Run the demo app with the android device selected. The app includes the library and all available + providers + +In case the android device is not detected by Android Studio: +- Make sure the device is in developer mode and has USB Debugging enabled +- Make sure the USB cable supports data transfer (some cables only support charging) + +## Adding to the ReadTheDocs documentation + +To test documentation changes locally, you need to install Sphinx and other dependencies. Using +python 3, you can run `pip install -r ./docs/requirements.txt` in your virtual environment. + +In order to build the documentation, run `sphinx-build -a ./docs `. + +To view the generated documentation, use the url `file:///index.html` in your web +browser. + +All documentation is written in markdown. Due to the lack of proper java support, the API reference +was "hacked" using default `method` directives for functions, and `cpp:var` or `cpp:enum` directives +for attributes and enums respectively. + +Note that anchor links to methods and members are not well supported, so in general it is better to +link to the parent class or interface rather than the method or attribute itself. + +:::{seealso} + +You may check the +[Sphinx](https://www.sphinx-doc.org/en/master/index.html) and +[MyST](https://myst-parser.readthedocs.io/en/latest/index.html) documentations for more details on +how to format documentation content. +::: + +## Adding another SDK / Scanner provider + +A scanner SDK contains at least a [`Scanner` implementation](api/scanner.md#the-scanner-api) +(interfacing between the code and the device) and a +[`ScannerProvider` implementation](api/scanner_provider.md#the-scannerprovider-api) (handling +scanner creation and device compatibility checks). + +In order for a new scanner SDK to be found by the library, the +[`ScannerProvider` implementation](api/scanner_provider.md#the-scannerprovider-api) implementation +needs to be declared as a service in its `AndroidManifest.xml`, with an intent-filter containing the +action `com.enioka.scan.PROVIDE_SCANNER` for "regular" devices, and +`com.enioka.scan.PROVIDE_SPP_SCANNER` for bluetooth devices. + +The associated Java class does not need to extend Android's `Service` class(the +`tools:ignore="Instantiatable"` attribute may be added to the service in the manifest), +but it must provide a public default constructor as it will be instantiated using `Class.getName()`. + +:::{seealso} + +* The [`Scanner` interface](api/scanner.md) documentation +* The [`ScannerProvider` interface](api/scanner_provider.md) documentation +* The sources of various scanner providers on the + [GitHub repository](https://github.com/enioka-Haute-Couture/enioka_scan), mainly the + [Mock SDK](https://github.com/enioka-Haute-Couture/enioka_scan/tree/master/enioka_scan_mock) +::: + +## Library tests + +While not every aspect of the code may be easily tested, some parts can and should be. For example, +parsers and translation methods used to convert enioka Scan API methods to device-specific commands +should be unit-tested to ensure payloads are properly handled. + +The repository contains GitHub Action pipelines to build the library and launch both JUnit and +Android Instrumented tests for both Android API levels 19 (the minimum supported version) and 28 +(the target version). + +## Release process + +Once enough features or fixes are merged into `master`, a new version tag will be pushed by a +repository owner/maintainer. This will trigger a GitHub Action workflow to create a release with an +artefact of the core library, and will upload all artefacts to Maven Central. + +:::{warning} + +The release tag ***must*** be pushed from the CLI, as a tag or release created from the +GitHub web UI will fail to trigger the workflow. +::: diff --git a/docs/guides/custom_layout.md b/docs/guides/custom_layout.md new file mode 100644 index 00000000..bc4a063e --- /dev/null +++ b/docs/guides/custom_layout.md @@ -0,0 +1,49 @@ +# Using a custom layout with the scanning activity + +In order to use a custom layout with the provided `ScannerCompatActivity`, two fields need to be +changed to point it. You can change these attributes in your activity's `onCreate()` lifecycle +step. + +```java +import com.enioka.scanner.activities.ScannerCompatActivity; + +public class MyScanningActivity extends ScannerCompatActivity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(); + layoutIdLaser = R.layout.activity_parcel_scan_laser; + layoutIdCamera = R.layout.activity_parcel_scan_camera; + } +} +``` + +Here, `layoutIdLaser` is the layout used when handling laser scanner devices, and `layoutIdCamera` +is the layout used when using the device's camera as a scanner. + +To ensure activity is able to properly switch to the camera when needed, you should include the +following view block in the `.xml` file used by `layoutIdCamera`: + +```xml + +``` + +Should you choose a different ID for this view, remember to also change the value of `cameraViewId` +in the activity. + +:::{seealso} + +* The [scanner activity](../api/scanner_activity.md) documentation +* The [camera](../api/camera.md) documentation +::: \ No newline at end of file diff --git a/docs/guides/index.md b/docs/guides/index.md new file mode 100644 index 00000000..35deb2c8 --- /dev/null +++ b/docs/guides/index.md @@ -0,0 +1,10 @@ +# Guides + +This sections contains quick guides to reach specific desired outcomes without having to dig too +deep in the [API reference](../api/index.md). + +:::{toctree} +:maxdepth: 1 + +custom_layout +::: \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index b02f0d80..47cd200b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,17 +3,15 @@ This library makes the integration of all barcode scanners easy in any Android application, avoiding vendor lock-in and lowering the cost of advanced scanner integration. -It is compatible with: -- Zebra EMDK devices (which comprise most of their integrated systems like the TC25, TC75, WT6000...) -- Zebra Bluetooth scanners -- Honeywell AIDC integrated devices (including CN* devices) -- Athesi SPA43 -- GeneralScan Bluetooth rings -- And a few others, check full compatibility table below. +It is compatible with a wide variety of scanning devices, integrated or external, from different +vendors such as Zebra, Honeywell, Athesi and more. -When there are no compatible hardware devices available, the library provides a camera reader based on ZBar (default) or ZXing. +When there are no compatible hardware devices available, the library provides a camera reader based +on [ZBar](https://mvnrepository.com/artifact/me.dm7.barcodescanner/zbar) (default) or +[ZXing](https://mvnrepository.com/artifact/com.google.zxing/core). -Through a common abstraction, it provides access to the following methods (provided the hardware supports them): +Through a common abstraction, it provides access to the following methods (provided the hardware +supports them): - press/release the scanner's trigger - pause/resume scanning abilities - disconnect/reconnect scanners @@ -21,11 +19,22 @@ Through a common abstraction, it provides access to the following methods (provi - enable/disable colored LEDs - set scanner enabled symbologies -Finally, it provides a ready to use Service that handles scanner lifecycles, as well as a template Activity, and a sample demo -application, allowing to use scanners in a matter of minutes. +Finally, it provides a ready to use Service that handles scanner lifecycles, as well as a template +Activity, and a demo application, allowing you to use scanners in a matter of minutes. -:::{admonition} WIP -:class: attention +:::{toctree} +:numbered: +:maxdepth: 3 +:titlesonly: -This documentation is a work in progress. +quickstart +dependencies +guides/index +api/index +develop +::: + +:::{seealso} + +* The [GitHub repository](https://github.com/enioka-Haute-Couture/enioka_scan) of the library ::: \ No newline at end of file diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 00000000..b4baff60 --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,138 @@ +# Quick Start + +This guide will show you the minimum required steps to include **enioka Scan** in your application. +It assumes that you already have a configured Android Studio project. + +This guide will only go over the minimal code. For a more in-depth guide for each use case, check +the [API reference](api/index.md). + +## Add the required dependencies + +In order to use **enioka Scan**, you need to add the corresponding dependency to your `build.gradle`. + +```groovy +repositories { + mavenCentral() +} +dependencies { + implementation 'com.enioka.scanner:scanner:2.4.1:aar' +} +``` + +Depending on your scanning devices, this single dependency may be enough. For some devices, other +dependencies are required, you can check the +[library dependencies and compatibility matrix](dependencies.md) for a detailed overview. + +## Using the library + +There are different ways to use the library, depending on where it has to be used. And in any ways, +the library does not hide its low-level objects which are an always available fallback. Most scanner +capabilities are exposed through the [`Scanner` API](api/scanner.md#the-scanner-interface), though +the default behavior of the +[`ScannerServiceApi` API](api/scanner_service.md#the-scannerserviceapi-interface) ought to be enough +for simply receiving scanned data. + +### Using enioka Scan in an activity + +The simplest way to use **enioka Scan** is to extend the provided +[`ScannerCompatActivity` activity](api/scanner_activity.md): + +```java +import com.enioka.scanner.activities.ScannerCompatActivity; + +public class MyScanningActivity extends ScannerCompatActivity { +} +``` + +This creates an activity with a very simple layout, which displays status messages from the scanners +as well as scanning results and some utility buttons: triggers for the scanner, a toggle for +illumination, and a beep trigger. + +To use your own layouts with this activity, you can follow [this guide](guides/custom_layout.md). + +Finally, note that inside the activity code there are a few hooks that can be overloaded - these are +public or protected methods, with full javadoc. Of notice are: + +* `onData(List data)` +* `onStatusChanged(Scanner, ScannerStatusCallback.Status)` + +:::{seealso} + +* The [`ScannerCompatActivity` activity](api/scanner_activity.md) documentation +* The [`ScannerClient` interface](api/scanner_service.md#the-scannerclient-interface) documentation +::: + +### Using enioka Scan outside of an activity + +The library exposes a service which can be bound as any other bound service. `ScannerCompatActivity` +actually uses this service to handle scanner connexions, and you are free to use it directly in your +application. + +**enioka Scan** provides a helper class that reduces the boilerplate code required to bind the +service to your application class: + +```java +public class App extends Application { + ScannerServiceBinderHelper serviceBinder; + + @Override + public void onCreate() { + super.onCreate(); + serviceBinder = ScannerServiceBinderHelper.bind(this); // a second overload exists with a configuration Bundle. + } + + // You may then call service methods directly + public void myMethod() { + serviceBinder.getScannerService().registerClient(/* ... */); + } +} +``` + +It is then possible to use the +[`ScannerServiceApi` instance](api/scanner_service.md#the-scannerserviceapi-interface) to access the +different endpoints of the service. The most interesting one is `registerClient()` which hooks +scanning callbacks and scanner/provider discovery notifications to a class implementing the +[`ScannerClient` interface](api/scanner_service.md#the-scannerclient-interface) such as a custom +activity. + +Please remember to unbind the service when it is not needed anymore, as for any other service. This +will often be in "onDestroy" hooks. Also, as this is a bound service, it is destroyed whenever it +has no bound clients left. Many applications actually bind the service on startup onto the +application context to be sure it is never destroyed and therefore is very quick to bind from +anywhere, but this depends on the use-case and is not compulsory at all. + +Finally, there are a few Intent Extra properties which can be set to control the behaviour of the +service such as filters used in the scanner search. These can be found as static strings inside +the [`ScannerServiceApi` interface](api/scanner_service.md#the-scannerserviceapi-interface), and +methods in the [`ScannerSearchOptions` class](api/scanner_service.md#the-scannersearchoptions-class) +help converting search parameters to and from those intent extras. + +:::{seealso} + +* The [Scanner Service API](api/scanner_service.md) documentation +::: + +## Using the camera + +When there is no laser scanner available, or when a button is clicked, the `ScannerCompatActivity` +activity will fallback to using the device camera (if any) to scan barcodes. This leverages two +different barcode scanning libraries, ZBar and ZXing in recent versions. It is compatible both with +very old Camera APIs as well as more recent Camera 2 APIs. It tries to set the best camera +parameters for barcode scanning, including dynamically setting the camera resolution according to +the processing speed of the device. As a result, it may take a few dozen seconds to reach the most +suitable settings on first startup - the resolution is then stored and reused on subsequent +initialisations. + +The camera scanner can actually be used easily inside your own activities without any links with the +rest of the library (no need to use the scanner service, etc) by just adding the +`CameraBarcodeScanView` to your layouts, and then registering a callback on this view using +`CameraBarcodeScanView.setResultHandler`. Other APIs are available to enable the torch or pause +scanning. + +The provided Camera activity will display a target rectangle, which can be moved or tapped to change +or refresh the autofocus area. + +:::{seealso} + +* The [camera](api/camera.md) documentation +::: \ No newline at end of file diff --git a/enioka_scan/src/main/java/com/enioka/scanner/activities/ScannerCompatActivity.java b/enioka_scan/src/main/java/com/enioka/scanner/activities/ScannerCompatActivity.java index 5ce91ebe..d3972585 100644 --- a/enioka_scan/src/main/java/com/enioka/scanner/activities/ScannerCompatActivity.java +++ b/enioka_scan/src/main/java/com/enioka/scanner/activities/ScannerCompatActivity.java @@ -73,7 +73,7 @@ public class ScannerCompatActivity extends AppCompatActivity implements ScannerC protected boolean goToCamera = false; /** - * Use bluetooth to look for scanners (if available). + * Check the app's bluetooth permissions. This variable does not affect scanner search options. */ protected boolean useBluetooth = true; @@ -95,13 +95,18 @@ public class ScannerCompatActivity extends AppCompatActivity implements ScannerC */ protected int cameraViewId = R.id.camera_scan_view; + /** + * The ID of the ImageButton on which to press to manually switch to camera mode. + */ + protected int cameraToggleId = R.id.scanner_bt_camera; + /** * The ID of the optional ImageButton on which to press to toggle the flashlight/illumination. */ protected int flashlightViewId = R.id.scanner_flashlight; /** - * The ID of the optional ImageButton on which to press to toggle the flashlight/illumination. + * The ID of the optional ImageButton on which to press to toggle the zxing/zbar camera scan library. */ protected int scannerModeToggleViewId = R.id.scanner_switch_zxing; @@ -168,9 +173,9 @@ protected void onStart() { return; } - if (useBluetooth && hasPermissionSet(this, PERMISSIONS_BT)) { + if (!useBluetooth || hasPermissionSet(this, PERMISSIONS_BT)) { bindAndStartService(); - } else { + } else if (useBluetooth && !hasPermissionSet(this, PERMISSIONS_BT)) { requestPermissionSet(this, PERMISSIONS_BT, PERMISSION_REQUEST_ID_BT); } } @@ -201,7 +206,7 @@ protected void onResume() { } // Immediately set some buttons (which do no need to wait for scanners). - resetCameraButton(); + displayCameraButton(); displayManualInputButton(); // Register this activity on the scanner service (hooks onData) and ask it to hook possible scanners needing foreground control onto this activity. @@ -397,7 +402,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis } case PERMISSION_REQUEST_ID_BT: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (useBluetooth && hasPermissionSet(this, PERMISSIONS_BT)) { + if (!useBluetooth || hasPermissionSet(this, PERMISSIONS_BT)) { bindAndStartService(); } } else { @@ -472,7 +477,7 @@ public void onData(List data) { /** * Display the torch button "on" or "off" is the device has capability. **/ - protected void displayTorch() { + private void displayTorch() { final ImageButton flashlight = findViewById(flashlightViewId); if (findViewById(flashlightViewId) == null) { return; @@ -543,7 +548,7 @@ protected boolean anyScannerHasIlluminationOn() { /** * Display a manual input (keyboard) button for manual input. */ - protected void displayManualInputButton() { + private void displayManualInputButton() { final View bt = findViewById(keyboardOpenViewId); if (bt == null) { return; @@ -577,13 +582,13 @@ public void dismiss() { /** * Display a "use camera" button to allow using camera input even when a laser is available. */ - protected void resetCameraButton() { - if (findViewById(R.id.scanner_bt_camera) != null) { - findViewById(R.id.scanner_bt_camera).setOnClickListener(view -> initCamera()); + private void displayCameraButton() { + if (findViewById(cameraToggleId) != null) { + findViewById(cameraToggleId).setOnClickListener(view -> initCamera()); } } - protected void displayCameraReaderToggle() { + private void displayCameraReaderToggle() { final Switch toggle = findViewById(scannerModeToggleViewId); if (toggle == null) { return; @@ -596,7 +601,7 @@ protected void displayCameraReaderToggle() { }); } - protected void displayCameraPauseToggle() { + private void displayCameraPauseToggle() { final Switch toggle = findViewById(scannerModeTogglePauseId); if (toggle == null) { return; diff --git a/enioka_scan/src/main/java/com/enioka/scanner/api/ScannerSearchOptions.java b/enioka_scan/src/main/java/com/enioka/scanner/api/ScannerSearchOptions.java index 5a8e952f..6a36f016 100644 --- a/enioka_scan/src/main/java/com/enioka/scanner/api/ScannerSearchOptions.java +++ b/enioka_scan/src/main/java/com/enioka/scanner/api/ScannerSearchOptions.java @@ -13,6 +13,11 @@ * A property bag tweaking the scanner search method. Some providers may ignore these options. */ public class ScannerSearchOptions { + /** + * If true, the service will start searching for scanners immediately upon binding. If false, the service will only discover available providers without requesting scanners immediately. + */ + public boolean startSearchOnServiceBind = true; + /** * If a scanner is known but not available, wait for it. If false, consider the scanner unavailable immediately. */ @@ -58,6 +63,11 @@ public class ScannerSearchOptions { */ public Set excludedProviderKeys = null; + /** + * An array of {@link com.enioka.scanner.data.BarcodeType BarcodeType} to activate. Ignored if null or empty. + */ + public Set symbologySelection = null; + public static ScannerSearchOptions defaultOptions() { return new ScannerSearchOptions(); } @@ -76,6 +86,7 @@ public ScannerSearchOptions getAllAvailableScanners() { public ScannerSearchOptions fromIntentExtras(final Intent intent) { Bundle extras = intent.getExtras(); if (extras != null) { + startSearchOnServiceBind = extras.getBoolean(ScannerServiceApi.EXTRA_START_SEARCH_ON_SERVICE_BIND, startSearchOnServiceBind); waitDisconnected = extras.getBoolean(ScannerServiceApi.EXTRA_SEARCH_WAIT_DISCONNECTED_BOOLEAN, waitDisconnected); returnOnlyFirst = extras.getBoolean(ScannerServiceApi.EXTRA_SEARCH_RETURN_ONLY_FIRST_BOOLEAN, returnOnlyFirst); useBlueTooth = extras.getBoolean(ScannerServiceApi.EXTRA_SEARCH_ALLOW_BT_BOOLEAN, useBlueTooth); @@ -95,6 +106,12 @@ public ScannerSearchOptions fromIntentExtras(final Intent intent) { excludedProviderKeys = new HashSet<>(); excludedProviderKeys.addAll(Arrays.asList(excludedProviderKeysArray)); } + + String[] symbologySelectionArray = extras.getStringArray(ScannerServiceApi.EXTRA_SYMBOLOGY_SELECTION); + if (symbologySelectionArray != null && symbologySelectionArray.length > 0) { + symbologySelection = new HashSet<>(); + symbologySelection.addAll(Arrays.asList(symbologySelectionArray)); + } } return this; @@ -107,6 +124,7 @@ public ScannerSearchOptions fromIntentExtras(final Intent intent) { * @param intent The intent to update */ public void toIntentExtras(final Intent intent) { + intent.putExtra(ScannerServiceApi.EXTRA_START_SEARCH_ON_SERVICE_BIND, startSearchOnServiceBind); intent.putExtra(ScannerServiceApi.EXTRA_SEARCH_WAIT_DISCONNECTED_BOOLEAN, waitDisconnected); intent.putExtra(ScannerServiceApi.EXTRA_SEARCH_RETURN_ONLY_FIRST_BOOLEAN, returnOnlyFirst); intent.putExtra(ScannerServiceApi.EXTRA_SEARCH_ALLOW_BT_BOOLEAN, useBlueTooth); @@ -119,5 +137,8 @@ public void toIntentExtras(final Intent intent) { if (excludedProviderKeys != null && excludedProviderKeys.size() > 0) { intent.putExtra(ScannerServiceApi.EXTRA_SEARCH_EXCLUDED_PROVIDERS_STRING_ARRAY, excludedProviderKeys.toArray(new String[0])); } + if (symbologySelection != null && symbologySelection.size() > 0) { + intent.putExtra(ScannerServiceApi.EXTRA_SYMBOLOGY_SELECTION, symbologySelection.toArray(new String[0])); + } } } diff --git a/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanView.java b/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanView.java index 26020c1c..f51a278c 100644 --- a/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanView.java +++ b/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanView.java @@ -184,4 +184,13 @@ public void resumeCamera() { public void resetTargetPosition() { this.proxiedView.resetTargetPosition(); } + + /** + * Get the JPEG data of the image used in the latest successful scan. + * + * @return a byte[] of JPEG data, or null if there is no previous scan data. + */ + public byte[] getLatestSuccessfulScanJpeg() { + return this.proxiedView.getLatestSuccessfulScanJpeg(); + } } diff --git a/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV1.java b/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV1.java index ee13da4d..ca51d95e 100644 --- a/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV1.java +++ b/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV1.java @@ -300,6 +300,7 @@ private void setAreas(Camera.Parameters prms) { } } + @Override protected void refreshAutofocusZone() { if (this.cam == null) { Log.w(VIEW_LOG_TAG, "refreshAutofocusZone: No camera instance, make sure camera was properly initialized and `cleanup()` or `closeCamera()` were not called previously"); @@ -331,6 +332,7 @@ protected void refreshAutofocusZone() { }); } + @Override public void setPreviewResolution(Point newResolution) { if (this.cam == null) { Log.w(VIEW_LOG_TAG, "setPreviewResolution: No camera instance, make sure camera was properly initialized and `cleanup()` or `closeCamera()` were not called previously"); @@ -501,6 +503,7 @@ public void onPreviewFrame(byte[] data, Camera camera) { frameAnalyser.handleFrame(ctx); } + @Override public void giveBufferBackInternal(FrameAnalysisContext ctx) { if (this.cam == null) { Log.w(VIEW_LOG_TAG, "giveBufferBackInternal: No camera instance, make sure camera was properly initialized and `cleanup()` or `closeCamera()` were not called previously"); @@ -517,6 +520,7 @@ public void giveBufferBackInternal(FrameAnalysisContext ctx) { //////////////////////////////////////////////////////////////////////////////////////////////// // Lifecycle external toggles + @Override public void pauseCamera() { if (this.cam != null) { this.cam.setPreviewCallbackWithBuffer(null); @@ -531,6 +535,7 @@ public void pauseCamera() { }); } + @Override public void resumeCamera() { if (this.cam != null) { this.cam.startPreview(); @@ -566,6 +571,7 @@ public void pauseScanner() { //////////////////////////////////////////////////////////////////////////////////////////////// // Clean up methods + @Override public void cleanUp() { if (this.cam == null) { Log.w(VIEW_LOG_TAG, "cleanup: No camera instance, make sure camera was properly initialized and `cleanup()` or `closeCamera()` were not called previously"); diff --git a/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV2.java b/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV2.java index c7127eba..98162541 100644 --- a/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV2.java +++ b/enioka_scan/src/main/java/com/enioka/scanner/camera/CameraBarcodeScanViewV2.java @@ -317,6 +317,7 @@ private MeteringRectangle getMeteringZone() { return new MeteringRectangle(x0, y0, x1 - x0, y1 - y0, 1000); } + @Override protected void refreshAutofocusZone() { if (anyAttributeMissing("refreshAutofocusZone")) { return; diff --git a/enioka_scan/src/main/java/com/enioka/scanner/service/ScannerServiceBinderHelper.java b/enioka_scan/src/main/java/com/enioka/scanner/service/ScannerServiceBinderHelper.java index 4294472d..1d93dda6 100644 --- a/enioka_scan/src/main/java/com/enioka/scanner/service/ScannerServiceBinderHelper.java +++ b/enioka_scan/src/main/java/com/enioka/scanner/service/ScannerServiceBinderHelper.java @@ -8,6 +8,8 @@ import android.os.Bundle; import android.os.IBinder; +import com.enioka.scanner.api.ScannerSearchOptions; + /** * A very simple helper intending to simplify boilerplate code when using the {@link ScannerService} at the application level. * Useless if not using the service but using directly {@link com.enioka.scanner.LaserScanner}. @@ -37,12 +39,9 @@ public void onServiceDisconnected(ComponentName name) { * Service default configuration, used everywhere else. A new bundle is returned on each call. */ public static Bundle defaultServiceConfiguration() { - Bundle res = new Bundle(); - res.putBoolean(ScannerServiceApi.EXTRA_SEARCH_ALLOW_BT_BOOLEAN, false); - res.putBoolean(ScannerServiceApi.EXTRA_SEARCH_ALLOW_INTENT_BOOLEAN, true); - res.putBoolean(ScannerServiceApi.EXTRA_SEARCH_KEEP_SEARCHING_BOOLEAN, false); - - return res; + Intent intent = new Intent(); + ScannerSearchOptions.defaultOptions().toIntentExtras(intent); + return intent.getExtras(); } private void actuallyBind(Application a, Bundle all) { @@ -53,15 +52,13 @@ private void actuallyBind(Application a, Bundle all) { public static ScannerServiceBinderHelper bind(Application a, Bundle extra) { ScannerServiceBinderHelper res = new ScannerServiceBinderHelper(); - Bundle b = defaultServiceConfiguration(); - b.putAll(extra); - res.actuallyBind(a, b); + res.actuallyBind(a, extra); return res; } public static ScannerServiceBinderHelper bind(Application a) { - return bind(a, new Bundle()); + return bind(a, defaultServiceConfiguration()); } public ScannerServiceApi getScannerService() {